diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 77ac72dd..3a64609d 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -1624,6 +1624,9 @@ class TMoverParameters double EpForce = 0.0; /*Poziom zadanej sily EP*/ bool Signalling = false; /*Czy jest zalaczona sygnalizacja hamowania ostatniego wagonu*/ bool Radio = false; /*Czy jest zalaczony radiotelefon*/ + // the cut-off in the radio's power supply: a fuse in the cabinet, a lever on the desk or the switch on the radio's power + // supply (eu07/maszyna#438). Closed unless the driver opens it, so vehicles without such a switch work as before + bool RadioFuse = true; float NominalBatteryVoltage = 0.f; /*Winger - baterie w elektrykach*/ TDimension Dim; /*wymiary*/ double Cx = 0.0; /*wsp. op. aerodyn.*/ @@ -2549,10 +2552,10 @@ class TMoverParameters auto const oilmanual{OilPump.start_type != start_t::automatic}; return (fuelmanual || oilmanual) && (false == fuelmanual || FuelPump.is_enabled) && (false == oilmanual || OilPump.is_enabled); } - // whether the radio is switched on and has the low voltage to work + // whether the radio is switched on, its power supply isn't cut off and it has the low voltage to work bool RadioPowered() const { - return Radio && (Power24vIsAvailable || Power110vIsAvailable); + return Radio && RadioFuse && (Power24vIsAvailable || Power110vIsAvailable); } /*obrot kol uwzgledniajacy poslizg*/ double ComputeRotatingWheel(double WForce, double dt, double n) const; diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 8b1eee98..ae32909f 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -3146,7 +3146,7 @@ void TMoverParameters::SecuritySystemCheck(double dt) bool isPower = Power24vIsAvailable || Power110vIsAvailable; SecuritySystem.update(dt, Vel, isPower, CabActive); - if (!Battery || !Radio) + if (!Battery || !Radio || !RadioFuse) { // wyłączenie baterii deaktywuje sprzęt RadiostopSwitch(false); } diff --git a/application/driverhints.cpp b/application/driverhints.cpp index bcfd7dbd..ae39ede7 100644 --- a/application/driverhints.cpp +++ b/application/driverhints.cpp @@ -138,6 +138,16 @@ TController::cue_action( driver_hint const Action, float const Actionparameter ) return mvOccupied->Radio == true; } ); break; } + case driver_hint::radiofuseon: { + if( AIControllFlag ) { + mvOccupied->RadioFuse = true; + } + hint( + Action, + [this](float const /*Parameter*/) { + return mvOccupied->RadioFuse == true; } ); + break; + } case driver_hint::radiochannel: { if( AIControllFlag ) { iRadioChannel = static_cast( Actionparameter ); diff --git a/application/driverhints_def.h b/application/driverhints_def.h index f59ed734..2ce89667 100644 --- a/application/driverhints_def.h +++ b/application/driverhints_def.h @@ -2,6 +2,7 @@ DRIVER_HINT_DEF(batteryon, STRN("Switch on battery")) DRIVER_HINT_DEF(batteryoff, STRN("Switch off battery")) DRIVER_HINT_DEF(radioon, STRN("Switch on radio")) DRIVER_HINT_DEF(radiooff, STRN("Switch off radio")) +DRIVER_HINT_DEF(radiofuseon, STRN("Close radio fuse")) DRIVER_HINT_DEF(radiochannel, STRN("Tune into channel %.0f")) DRIVER_HINT_DEF(oilpumpon, STRN("Switch on oil pump")) DRIVER_HINT_DEF(oilpumpoff, STRN("Switch off oil pump")) diff --git a/application/driveruipanels.cpp b/application/driveruipanels.cpp index e318d356..8186b82c 100644 --- a/application/driveruipanels.cpp +++ b/application/driveruipanels.cpp @@ -810,7 +810,7 @@ debug_panel::update_section_vehicle( std::vector &Output ) { mover.HeatingAllow ? 'h' : '.', mover.Hamulec->Releaser() ? '^' : '.', - std::string( m_input.mechanik ? STR(" R") + ( mover.Radio ? std::to_string( m_input.mechanik->iRadioChannel ) : "-" ) : "" ).c_str(), + std::string( m_input.mechanik ? STR(" R") + ( mover.Radio && mover.RadioFuse ? std::to_string( m_input.mechanik->iRadioChannel ) : "-" ) : "" ).c_str(), std::string( isdieselenginepowered ? STR(" oil pressure: ") + to_string( mover.OilPump.pressure, 2 ) : "" ).c_str(), // power transfers // 3000v diff --git a/input/command.cpp b/input/command.cpp index ad208ce9..7d3fe7d1 100644 --- a/input/command.cpp +++ b/input/command.cpp @@ -158,6 +158,9 @@ commanddescription_sequence Commands_descriptions = { {"radiovolumeincrease", command_target::vehicle, command_mode::oneoff}, {"radiovolumedecrease", command_target::vehicle, command_mode::oneoff}, {"radiovolumeset", command_target::vehicle, command_mode::oneoff}, + {"radiofusetoggle", command_target::vehicle, command_mode::oneoff}, + {"radiofuseenable", command_target::vehicle, command_mode::oneoff}, + {"radiofusedisable", command_target::vehicle, command_mode::oneoff}, {"cabchangeforward", command_target::vehicle, command_mode::oneoff}, {"cabchangebackward", command_target::vehicle, command_mode::oneoff}, {"modernlightdimmerdecrease", command_target::vehicle, command_mode::oneoff}, @@ -550,6 +553,9 @@ std::unordered_map const commandMap = { {"radiovolumeincrease", user_command::radiovolumeincrease}, {"radiovolumedecrease", user_command::radiovolumedecrease}, {"radiovolumeset", user_command::radiovolumeset}, + {"radiofusetoggle", user_command::radiofusetoggle}, + {"radiofuseenable", user_command::radiofuseenable}, + {"radiofusedisable", user_command::radiofusedisable}, {"cabchangeforward", user_command::cabchangeforward}, {"cabchangebackward", user_command::cabchangebackward}, {"viewturn", user_command::viewturn}, diff --git a/input/command.h b/input/command.h index ea51a68c..aaf361e2 100644 --- a/input/command.h +++ b/input/command.h @@ -151,6 +151,9 @@ enum class user_command radiovolumeincrease, radiovolumedecrease, radiovolumeset, + radiofusetoggle, + radiofuseenable, + radiofusedisable, cabchangeforward, cabchangebackward, diff --git a/input/driverkeyboardinput.cpp b/input/driverkeyboardinput.cpp index e7e636b3..047f3678 100644 --- a/input/driverkeyboardinput.cpp +++ b/input/driverkeyboardinput.cpp @@ -160,6 +160,9 @@ driverkeyboard_input::default_bindings() { // radiovolumeincrease, // radiovolumedecrease, // radiovolumeset, + // radiofusetoggle, + // radiofuseenable, + // radiofusedisable, { cabchangeforward, {GLFW_KEY_HOME, ""} }, { cabchangebackward, {GLFW_KEY_END, ""} }, // viewturn, diff --git a/input/drivermouseinput.cpp b/input/drivermouseinput.cpp index c7a9aeb9..222c58a6 100644 --- a/input/drivermouseinput.cpp +++ b/input/drivermouseinput.cpp @@ -801,6 +801,15 @@ drivermouse_input::default_bindings() { { "radiooff_sw:", { radiodisable, none } }, + { "radiofuse_sw:", { + radiofusetoggle, + none } }, + { "radiofuseon_sw:", { + radiofuseenable, + none } }, + { "radiofuseoff_sw:", { + radiofusedisable, + none } }, { "radiochannel_sw:", { radiochannelincrease, radiochanneldecrease } }, diff --git a/tests/unit/test_radio.cpp b/tests/unit/test_radio.cpp index 33a12f72..b65cc2ce 100644 --- a/tests/unit/test_radio.cpp +++ b/tests/unit/test_radio.cpp @@ -26,3 +26,33 @@ TEST_CASE("radio: a radio switched off or without the low voltage doesn't") { } CHECK_FALSE(vehicle->RadioPowered()); } + +// the cut-off in the radio's power supply (eu07/maszyna#438): a fuse, a desk lever or the switch on the radio's +// power supply, closed unless the driver opens it + +TEST_CASE("radio: the radio fuse is closed by default") { + test::vehicle vehicle{test::definition::load("wagon")}; + CHECK(vehicle->RadioFuse); +} + +TEST_CASE("radio: an opened radio fuse cuts the radio off") { + test::vehicle vehicle{test::definition::load("wagon")}; + vehicle->Radio = true; + vehicle->Power24vIsAvailable = true; + vehicle->RadioFuse = false; + CHECK_FALSE(vehicle->RadioPowered()); + vehicle->RadioFuse = true; + CHECK(vehicle->RadioPowered()); +} + +TEST_CASE("radio: opening the radio fuse at a standstill resets the radio-stop, as switching the radio off does") { + test::vehicle loco{test::definition::fiz("4e_v1/4e")}; + loco->Battery = true; + loco->Radio = true; + REQUIRE(loco->RadiostopSwitch(true)); + loco->SecuritySystemCheck(0.1); + CHECK(loco->RadioStopFlag); + loco->RadioFuse = false; + loco->SecuritySystemCheck(0.1); + CHECK_FALSE(loco->RadioStopFlag); +} diff --git a/utilities/translation.cpp b/utilities/translation.cpp index d05ee13e..e172eabf 100644 --- a/utilities/translation.cpp +++ b/utilities/translation.cpp @@ -277,6 +277,11 @@ std::string locale::label_cab_control(std::string const &Label) { "mainb_sw:", STRN("line breaker B") }, { "mainc_sw:", STRN("line breaker C") }, { "radio_sw:", STRN("radio") }, + { "radioon_sw:", STRN("radio") }, + { "radiooff_sw:", STRN("radio") }, + { "radiofuse_sw:", STRN("radio fuse") }, + { "radiofuseon_sw:", STRN("radio fuse") }, + { "radiofuseoff_sw:", STRN("radio fuse") }, { "radiochannel_sw:", STRN("radio channel") }, { "radiochannelprev_sw:", STRN("radio channel") }, { "radiochannelnext_sw:", STRN("radio channel") }, diff --git a/vehicle/Driver.cpp b/vehicle/Driver.cpp index 0e72198a..110193af 100644 --- a/vehicle/Driver.cpp +++ b/vehicle/Driver.cpp @@ -2769,6 +2769,7 @@ bool TController::PrepareEngine() cue_action( driver_hint::batteryon ); cue_action( driver_hint::cabactivation); + cue_action( driver_hint::radiofuseon ); cue_action( driver_hint::radioon ); if( has_diesel_engine() ) { @@ -6450,13 +6451,19 @@ TController::control_security_system( double const Timedelta ) { } } if( iEngineActive - && false == mvOccupied->Radio + && ( false == mvOccupied->Radio || false == mvOccupied->RadioFuse ) && false == mvOccupied->RadioStopFlag ) { // otherwise if it's safe to do so, turn the radio back on // arbitrary 5 sec delay before switching radio back on m_radiocontroltime = std::min( m_radiocontroltime, 5.0 ); if( m_radiocontroltime < 0.0 ) { - cue_action( driver_hint::radioon ); + // hint at whichever of the two is open, the radio's switch or the cut-off in its power supply + if( false == mvOccupied->RadioFuse ) { + cue_action( driver_hint::radiofuseon ); + } + if( false == mvOccupied->Radio ) { + cue_action( driver_hint::radioon ); + } } else { m_radiocontroltime -= Timedelta; diff --git a/vehicle/DynObj.cpp b/vehicle/DynObj.cpp index ad1b535c..38122c45 100644 --- a/vehicle/DynObj.cpp +++ b/vehicle/DynObj.cpp @@ -7278,7 +7278,8 @@ void TDynamicObject::RadioStop() if( Mechanik ) { // o ile ktoś go prowadzi if( MoverParameters->SecuritySystem.radiostop_available() - && MoverParameters->Radio ) { + && MoverParameters->Radio + && MoverParameters->RadioFuse ) { // jeśli pojazd ma RadioStop i jest on aktywny // HACK cast until math types unification glm::dvec3 pos = static_cast(vPosition); diff --git a/vehicle/Train.cpp b/vehicle/Train.cpp index 1ff884e2..b81a3e9a 100644 --- a/vehicle/Train.cpp +++ b/vehicle/Train.cpp @@ -497,6 +497,9 @@ TTrain::commandhandler_map const TTrain::m_commandhandlers = { {user_command::radiovolumeincrease, &TTrain::OnCommand_radiovolumeincrease}, {user_command::radiovolumedecrease, &TTrain::OnCommand_radiovolumedecrease}, {user_command::radiovolumeset, &TTrain::OnCommand_radiovolumeset}, + {user_command::radiofusetoggle, &TTrain::OnCommand_radiofusetoggle}, + {user_command::radiofuseenable, &TTrain::OnCommand_radiofuseenable}, + {user_command::radiofusedisable, &TTrain::OnCommand_radiofusedisable}, {user_command::cabchangeforward, &TTrain::OnCommand_cabchangeforward}, {user_command::cabchangebackward, &TTrain::OnCommand_cabchangebackward}, {user_command::generictoggle0, &TTrain::OnCommand_generictoggle}, @@ -797,6 +800,7 @@ std::shared_ptr TTrain::GetTrainState(dictionary_source const } } dict->insert("radio", mvOccupied->Radio); + dict->insert("radio_fuse", mvOccupied->RadioFuse); dict->insert("radio_channel", RadioChannel()); dict->insert("radio_volume", m_radiovolume); dict->insert("door_lock", mvOccupied->Doors.lock_enabled); @@ -8236,41 +8240,101 @@ void TTrain::OnCommand_radiotoggle(TTrain *Train, command_data const &Command) // NOTE: works without the switch in the 3d model too, to allow system reset after receiving radio-stop signal // only reacting to press, so the sound can loop uninterrupted - if (false == Train->mvOccupied->Radio) - { - // turn on - OnCommand_radioenable(Train, Command); - } - else - { - // turn off - OnCommand_radiodisable(Train, Command); - } + // NOTE: flips the state directly rather than through radioenable/radiodisable, so it doesn't move the radioon_sw:/radiooff_sw: buttons + Train->mvOccupied->Radio = (false == Train->mvOccupied->Radio); } void TTrain::OnCommand_radioenable(TTrain *Train, command_data const &Command) { - if (Command.action != GLFW_PRESS) - { - return; - } - - if (false == Train->mvOccupied->Radio) + if (Command.action == GLFW_PRESS) { Train->mvOccupied->Radio = true; + // visual feedback + if (Train->m_controlmapper.contains("radioon_sw:")) + { + Train->ggRadioOnButton.UpdateValue(1.0f, Train->dsbSwitch); + } + } + else if (Command.action == GLFW_RELEASE) + { + if (Train->m_controlmapper.contains("radioon_sw:")) + { + Train->ggRadioOnButton.UpdateValue(0.0f, Train->dsbSwitch); + } } } void TTrain::OnCommand_radiodisable(TTrain *Train, command_data const &Command) +{ + if (Command.action == GLFW_PRESS) + { + Train->mvOccupied->Radio = false; + // visual feedback + if (Train->m_controlmapper.contains("radiooff_sw:")) + { + Train->ggRadioOffButton.UpdateValue(1.0f, Train->dsbSwitch); + } + } + else if (Command.action == GLFW_RELEASE) + { + if (Train->m_controlmapper.contains("radiooff_sw:")) + { + Train->ggRadioOffButton.UpdateValue(0.0f, Train->dsbSwitch); + } + } +} + +// the cut-off in the radio's power supply (eu07/maszyna#438); opening it works like switching the radio off, the +// radio-stop flag included +void TTrain::OnCommand_radiofusetoggle(TTrain *Train, command_data const &Command) { if (Command.action != GLFW_PRESS) { return; } - if (Train->mvOccupied->Radio) + // NOTE: flips the state directly rather than through radiofuseenable/radiofusedisable, so it doesn't move the + // radiofuseon_sw:/radiofuseoff_sw: buttons + Train->mvOccupied->RadioFuse = (false == Train->mvOccupied->RadioFuse); +} + +void TTrain::OnCommand_radiofuseenable(TTrain *Train, command_data const &Command) +{ + if (Command.action == GLFW_PRESS) { - Train->mvOccupied->Radio = false; + Train->mvOccupied->RadioFuse = true; + // visual feedback + if (Train->m_controlmapper.contains("radiofuseon_sw:")) + { + Train->ggRadioFuseOnButton.UpdateValue(1.0f, Train->dsbSwitch); + } + } + else if (Command.action == GLFW_RELEASE) + { + if (Train->m_controlmapper.contains("radiofuseon_sw:")) + { + Train->ggRadioFuseOnButton.UpdateValue(0.0f, Train->dsbSwitch); + } + } +} + +void TTrain::OnCommand_radiofusedisable(TTrain *Train, command_data const &Command) +{ + if (Command.action == GLFW_PRESS) + { + Train->mvOccupied->RadioFuse = false; + // visual feedback + if (Train->m_controlmapper.contains("radiofuseoff_sw:")) + { + Train->ggRadioFuseOffButton.UpdateValue(1.0f, Train->dsbSwitch); + } + } + else if (Command.action == GLFW_RELEASE) + { + if (Train->m_controlmapper.contains("radiofuseoff_sw:")) + { + Train->ggRadioFuseOffButton.UpdateValue(0.0f, Train->dsbSwitch); + } } } @@ -9298,9 +9362,9 @@ bool TTrain::Update(double const Deltatime) // KURS90 btLampkaMaxSila.Turn(abs(mvControlled->Im) >= 350); btLampkaPrzekrMaxSila.Turn(abs(mvControlled->Im) >= 450); - btLampkaRadio.Turn(mvOccupied->Radio); + btLampkaRadio.Turn(mvOccupied->Radio && mvOccupied->RadioFuse); btLampkaRadioMessage.Turn(radio_message_played); - btLampkaRadioStop.Turn(mvOccupied->Radio && mvOccupied->RadioStopFlag); + btLampkaRadioStop.Turn(mvOccupied->Radio && mvOccupied->RadioFuse && mvOccupied->RadioStopFlag); btLampkaHamulecReczny.Turn(mvOccupied->ManualBrakePos > 0); // NBMX wrzesien 2003 - drzwi oraz sygnał odjazdu if (DynamicObject->Mechanik != nullptr) @@ -9785,6 +9849,10 @@ bool TTrain::Update(double const Deltatime) ggRadioVolumeSelector.Update(); ggRadioVolumePrevious.Update(); ggRadioVolumeNext.Update(); + ggRadioOnButton.Update(); + ggRadioOffButton.Update(); + ggRadioFuseOnButton.Update(); + ggRadioFuseOffButton.Update(); ggDepartureSignalButton.Update(); /* ggPantFrontButton.Update(); @@ -11332,6 +11400,10 @@ void TTrain::clear_cab_controls() ggRadioVolumeSelector.Clear(); ggRadioVolumePrevious.Clear(); ggRadioVolumeNext.Clear(); + ggRadioOnButton.Clear(); + ggRadioOffButton.Clear(); + ggRadioFuseOnButton.Clear(); + ggRadioFuseOffButton.Clear(); ggDoorLeftPermitButton.Clear(); ggDoorRightPermitButton.Clear(); ggDoorPermitPresetButton.Clear(); @@ -12099,6 +12171,10 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con {"radiovolume_sw:", ggRadioVolumeSelector}, {"radiovolumeprev_sw:", ggRadioVolumePrevious}, {"radiovolumenext_sw:", ggRadioVolumeNext}, + {"radioon_sw:", ggRadioOnButton}, + {"radiooff_sw:", ggRadioOffButton}, + {"radiofuseon_sw:", ggRadioFuseOnButton}, + {"radiofuseoff_sw:", ggRadioFuseOffButton}, /* { "pantfront_sw:", ggPantFrontButton }, { "pantrear_sw:", ggPantRearButton }, @@ -12256,6 +12332,7 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con {"pantfrontoff_sw:", &mvPantographUnit->Pantographs[end::front].valve.is_disabled}, {"pantrearoff_sw:", &mvPantographUnit->Pantographs[end::rear].valve.is_disabled}, {"radio_sw:", &mvOccupied->Radio}, + {"radiofuse_sw:", &mvOccupied->RadioFuse}, {"cablight_sw:", &Cabine[iCabn].bLight}, {"springbraketoggle_bt:", &mvOccupied->SpringBrake.Activate}, {"couplingdisconnect_sw:", &m_couplingdisconnect}, diff --git a/vehicle/Train.h b/vehicle/Train.h index 0048787e..dd077e36 100644 --- a/vehicle/Train.h +++ b/vehicle/Train.h @@ -499,6 +499,9 @@ class TTrain { static void OnCommand_radiovolumeincrease(TTrain *Train, command_data const &Command); static void OnCommand_radiovolumedecrease(TTrain *Train, command_data const &Command); static void OnCommand_radiovolumeset(TTrain *Train, command_data const &Command); + static void OnCommand_radiofusetoggle(TTrain *Train, command_data const &Command); + static void OnCommand_radiofuseenable(TTrain *Train, command_data const &Command); + static void OnCommand_radiofusedisable(TTrain *Train, command_data const &Command); static void OnCommand_cabchangeforward( TTrain *Train, command_data const &Command ); static void OnCommand_cabchangebackward( TTrain *Train, command_data const &Command ); static void OnCommand_generictoggle( TTrain *Train, command_data const &Command ); @@ -614,6 +617,10 @@ public: // reszta może by?publiczna TGauge ggRadioVolumeSelector; TGauge ggRadioVolumePrevious; TGauge ggRadioVolumeNext; + TGauge ggRadioOnButton; + TGauge ggRadioOffButton; + TGauge ggRadioFuseOnButton; + TGauge ggRadioFuseOffButton; TGauge ggUpperLightButton; TGauge ggLeftLightButton; TGauge ggRightLightButton; diff --git a/vr/openvr_imp.cpp b/vr/openvr_imp.cpp index 02044d6e..b3af171b 100644 --- a/vr/openvr_imp.cpp +++ b/vr/openvr_imp.cpp @@ -687,6 +687,15 @@ std::unordered_map vr_openvr::m_buttonb { "radiooff_sw:", { user_command::radiodisable, user_command::none } }, + { "radiofuse_sw:", { + user_command::radiofusetoggle, + user_command::none } }, + { "radiofuseon_sw:", { + user_command::radiofuseenable, + user_command::none } }, + { "radiofuseoff_sw:", { + user_command::radiofusedisable, + user_command::none } }, { "radiochannel_sw:", { user_command::radiochannelincrease, user_command::radiochanneldecrease } },