mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-15 02:29:07 +01:00
feat: bistable hold coil lever of the line breaker (eu07/maszyna#364)
main_off_bt declared with "type: toggle" in the cab's .mmd is now the line breaker's hold coil lever of the ET22 and EP09: it stays where it is put. Off, it opens the line breaker and keeps it from closing (also cancelling a closing armed by a held main_on_bt); back on, it only feeds the coil again. The single keyboard key puts the lever back on before closing, and a line breaker closed by the AI or through the control cable puts the lever back on to match. TGauge now remembers whether type: was given, since every control defaults to toggle. Without type: on main_off_bt (all vehicles today) nothing changes. Data (SVN, not part of this commit): add "type: toggle" to main_off_bt: in dynamic/pkp/et22_v3/*.mmd and dynamic/pkp/ep09_v1, ep09_v2/*.mmd. Older exes ignore it.
This commit is contained in:
30
tests/unit/test_hold_coil_lever.cpp
Normal file
30
tests/unit/test_hold_coil_lever.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include "stdafx.h"
|
||||
#include "doctest.h"
|
||||
|
||||
#include "vehicle/holdcoillever.h"
|
||||
|
||||
// the lever of the line breaker's hold coil on the ET22 and the EP09 stays where it's put (eu07/maszyna#364), when the
|
||||
// cab declares its main_off_bt control with "type: toggle"; otherwise main_off_bt is the push button it always was
|
||||
|
||||
TEST_CASE("hold coil lever: only a main_off_bt declared as a toggle switch is the bistable lever (eu07/maszyna#364)") {
|
||||
CHECK(hold_coil_lever::is_bistable(true, true));
|
||||
// declared as a push button
|
||||
CHECK_FALSE(hold_coil_lever::is_bistable(true, false));
|
||||
// no type given: a control defaults to the toggle type, which existing cabs relied on for the push button
|
||||
CHECK_FALSE(hold_coil_lever::is_bistable(false, true));
|
||||
}
|
||||
|
||||
TEST_CASE("hold coil lever: operated, it goes over to the other position and stays there") {
|
||||
// on: the hold coil fed, the line breaker can close
|
||||
CHECK_FALSE(hold_coil_lever::cuts_off(0.f));
|
||||
auto const off{hold_coil_lever::operated(0.f)};
|
||||
CHECK(off == doctest::Approx(1.f));
|
||||
CHECK(hold_coil_lever::cuts_off(off));
|
||||
// operated again it goes back on
|
||||
auto const on{hold_coil_lever::operated(off)};
|
||||
CHECK(on == doctest::Approx(0.f));
|
||||
CHECK_FALSE(hold_coil_lever::cuts_off(on));
|
||||
// caught on the way between the positions it goes to the far one
|
||||
CHECK(hold_coil_lever::operated(0.3f) == doctest::Approx(1.f));
|
||||
CHECK(hold_coil_lever::operated(0.7f) == doctest::Approx(0.f));
|
||||
}
|
||||
@@ -249,6 +249,7 @@ TGauge::Load_mapping( cParser &Input, TGauge::scratch_data &Scratchpad ) {
|
||||
gaugetype == "delayed" ? push_delayed :
|
||||
gaugetype == "pushtoggle" ? pushtoggle :
|
||||
toggle; // "toggle" and default
|
||||
m_typedeclared = true;
|
||||
}
|
||||
else if( key == "soundinc:" ) {
|
||||
m_soundfxincrease.deserialize( Input, sound_type::single );
|
||||
|
||||
@@ -58,6 +58,9 @@ public:
|
||||
// returns offset of submodel associated with the button from the model centre
|
||||
glm::vec3 model_offset() const;
|
||||
TGaugeType type() const;
|
||||
// whether the definition of the control declared its type, rather than left it at the default
|
||||
bool type_declared() const {
|
||||
return m_typedeclared; }
|
||||
inline
|
||||
bool is_push() const {
|
||||
return ( static_cast<int>( type() ) & static_cast<int>( TGaugeType::push ) ) != 0; }
|
||||
@@ -87,6 +90,7 @@ private:
|
||||
// members
|
||||
TGaugeAnimation m_animation { TGaugeAnimation::gt_Unknown }; // typ ruchu
|
||||
TGaugeType m_type { TGaugeType::toggle }; // switch type
|
||||
bool m_typedeclared { false }; // the type was given with type: in the definition
|
||||
float m_friction { 0.f }; // hamowanie przy zliżaniu się do zadanej wartości
|
||||
float m_targetvalue { 0.f }; // wartość docelowa
|
||||
float m_value { 0.f }; // wartość obecna
|
||||
|
||||
@@ -15,6 +15,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "stdafx.h"
|
||||
#include "vehicle/Train.h"
|
||||
#include "vehicle/horn.h"
|
||||
#include "vehicle/holdcoillever.h"
|
||||
|
||||
#include "utilities/Globals.h"
|
||||
#include "simulation/simulation.h"
|
||||
@@ -3646,6 +3647,15 @@ void TTrain::OnCommand_pantographcompressoractivate(TTrain *Train, command_data
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
// whether the cab's main_off_bt is the bistable lever of the line breaker's hold coil (eu07/maszyna#364)
|
||||
bool is_hold_coil_lever(TGauge const &Button)
|
||||
{
|
||||
return Button.SubModel != nullptr && hold_coil_lever::is_bistable(Button.type_declared(), Button.type() == TGaugeType::toggle);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void TTrain::OnCommand_linebreakertoggle(TTrain *Train, command_data const &Command)
|
||||
{
|
||||
|
||||
@@ -3655,6 +3665,11 @@ void TTrain::OnCommand_linebreakertoggle(TTrain *Train, command_data const &Comm
|
||||
if (Train->m_linebreakerstate == 0)
|
||||
{
|
||||
// ...to close the circuit
|
||||
if (is_hold_coil_lever(Train->ggMainOffButton) && hold_coil_lever::cuts_off(Train->ggMainOffButton.GetDesiredValue()))
|
||||
{
|
||||
// the one key works both controls: the hold coil lever goes back on first, or the line breaker couldn't close
|
||||
Train->ggMainOffButton.UpdateValue(0.0, Train->dsbSwitch);
|
||||
}
|
||||
// NOTE: bit of a dirty shortcut here
|
||||
OnCommand_linebreakerclose(Train, Command);
|
||||
}
|
||||
@@ -3694,7 +3709,18 @@ void TTrain::OnCommand_linebreakeropen(TTrain *Train, command_data const &Comman
|
||||
if (Command.action == GLFW_PRESS)
|
||||
{
|
||||
// visual feedback
|
||||
if (Train->ggMainOffButton.SubModel != nullptr)
|
||||
if (is_hold_coil_lever(Train->ggMainOffButton))
|
||||
{
|
||||
// the bistable lever goes over to its other position and stays there
|
||||
auto const position{hold_coil_lever::operated(Train->ggMainOffButton.GetDesiredValue())};
|
||||
Train->ggMainOffButton.UpdateValue(position, Train->dsbSwitch);
|
||||
if (false == hold_coil_lever::cuts_off(position))
|
||||
{
|
||||
// the hold coil is fed again, which by itself doesn't close the line breaker
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (Train->ggMainOffButton.SubModel != nullptr)
|
||||
{
|
||||
Train->ggMainOffButton.UpdateValue(1.0, Train->dsbSwitch);
|
||||
}
|
||||
@@ -3731,8 +3757,9 @@ void TTrain::OnCommand_linebreakeropen(TTrain *Train, command_data const &Comman
|
||||
{
|
||||
Train->ggMainOnButton.UpdateValue(0.0, Train->dsbSwitch);
|
||||
}
|
||||
if (Train->ggMainOffButton.SubModel != nullptr)
|
||||
if (Train->ggMainOffButton.SubModel != nullptr && false == is_hold_coil_lever(Train->ggMainOffButton))
|
||||
{
|
||||
// (the bistable hold coil lever stays where it was put)
|
||||
Train->ggMainOffButton.UpdateValue(0.0, Train->dsbSwitch);
|
||||
}
|
||||
// and the two-state switch too, for good measure
|
||||
@@ -8365,6 +8392,20 @@ bool TTrain::Update(double const Deltatime)
|
||||
// because the state of the line breaker isn't changed to match, we need to do it here manually
|
||||
m_linebreakerstate = 0;
|
||||
}
|
||||
if (is_hold_coil_lever(ggMainOffButton) && hold_coil_lever::cuts_off(ggMainOffButton.GetDesiredValue()))
|
||||
{
|
||||
if (true == mvControlled->Mains)
|
||||
{
|
||||
// the line breaker was closed with the hold coil lever off, by the AI or through the control cable:
|
||||
// put the lever back on to match, like the line breaker state above
|
||||
ggMainOffButton.UpdateValue(0.0);
|
||||
}
|
||||
else if (m_linebreakerstate == 2)
|
||||
{
|
||||
// the hold coil was cut off while the closing button was held, the line breaker won't close on its release
|
||||
m_linebreakerstate = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ((ggMainButton.SubModel != nullptr && ggMainButton.GetDesiredValue() > 0.95) ||
|
||||
ggMainOnButton.SubModel != nullptr && ggMainOnButton.GetDesiredValue() > 0.95 ||
|
||||
|
||||
34
vehicle/holdcoillever.h
Normal file
34
vehicle/holdcoillever.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// the lever of the line breaker's hold coil (eu07/maszyna#364), as on the ET22 and the EP09: the cab's main_off_bt
|
||||
// control declared with "type: toggle" stays in the position it's put in. Position 0 keeps the hold coil fed, so the
|
||||
// line breaker can be closed; position 1 cuts the coil off, which opens the line breaker and keeps it from closing
|
||||
// until the lever is put back. Without the declared type main_off_bt stays the push button it always was, which
|
||||
// opens the line breaker and springs back.
|
||||
namespace hold_coil_lever {
|
||||
|
||||
// whether the main_off_bt control is the bistable lever, from the type its definition declares, if it declares one
|
||||
inline bool is_bistable(bool const Typedeclared, bool const Toggletype) {
|
||||
return Typedeclared && Toggletype;
|
||||
}
|
||||
|
||||
// whether the lever in the given position cuts off the hold coil
|
||||
inline bool cuts_off(float const Position) {
|
||||
return Position > 0.5f;
|
||||
}
|
||||
|
||||
// the position the lever goes to when it's operated: the other one
|
||||
inline float operated(float const Position) {
|
||||
return cuts_off(Position) ? 0.f : 1.f;
|
||||
}
|
||||
|
||||
} // namespace hold_coil_lever
|
||||
Reference in New Issue
Block a user