diff --git a/Driver.cpp b/Driver.cpp index b5db66dc..d1747092 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -2537,8 +2537,9 @@ bool TController::PrepareEngine() } if (mvControlling->EnginePowerSource.SourceType == TPowerSource::CurrentCollector) { // jeśli silnikowy jest pantografującym - mvOccupied->PantFront( true ); - mvOccupied->PantRear( true ); + mvControlling->OperatePantographsValve( operation_t::enable ); + mvControlling->OperatePantographValve( end::front, operation_t::enable ); + mvControlling->OperatePantographValve( end::rear, operation_t::enable ); if (mvControlling->PantPress < 4.2) { // załączenie małej sprężarki if( false == mvControlling->PantAutoValve ) { @@ -2726,8 +2727,8 @@ bool TController::ReleaseEngine() { // line breaker/engine OK = mvControlling->MainSwitch( false ); if( mvControlling->EnginePowerSource.SourceType == TPowerSource::CurrentCollector ) { - mvControlling->PantFront( false ); - mvControlling->PantRear( false ); + mvControlling->OperatePantographValve( end::front, operation_t::disable ); + mvControlling->OperatePantographValve( end::rear, operation_t::disable ); } } else { @@ -3064,9 +3065,9 @@ bool TController::DecBrakeEIM() mvOccupied->MainCtrlPos = 3; break; case 3: - OK = mvOccupied->MainCtrlPos < mvOccupied->MaxMainCtrlPosNoDirChange; + OK = mvOccupied->MainCtrlPos < mvOccupied->MainCtrlMaxDirChangePos; if( OK ) - mvOccupied->MainCtrlPos = mvOccupied->MaxMainCtrlPosNoDirChange; + mvOccupied->MainCtrlPos = mvOccupied->MainCtrlMaxDirChangePos; break; } return OK; @@ -4685,42 +4686,59 @@ TController::UpdateSituation(double dt) { if (mvControlling->EnginePowerSource.SourceType == TPowerSource::CurrentCollector) { + auto const useregularpantographlayout { + ( pVehicle->NextC( coupling::control ) == nullptr ) // standalone + || ( mvControlling->TrainType == dt_EZT ) // special case + || ( mvControlling->TrainType == dt_ET41 ) }; // special case + if( mvOccupied->Vel > 0.05 ) { // is moving if( ( fOverhead2 >= 0.0 ) || iOverheadZero ) { // jeśli jazda bezprądowa albo z opuszczonym pantografem ZeroSpeed(); } + if( ( fOverhead2 > 0.0 ) || iOverheadDown ) { // jazda z opuszczonymi pantografami - mvControlling->PantFront( false ); - mvControlling->PantRear( false ); + mvControlling->OperatePantographValve( end::front, operation_t::disable ); + mvControlling->OperatePantographValve( end::rear, operation_t::disable ); } else { // jeśli nie trzeba opuszczać pantografów // jazda na tylnym - if( iDirection >= 0 ) { + if( ( iDirection >= 0 ) && ( useregularpantographlayout ) ) { // jak jedzie w kierunku sprzęgu 0 - mvControlling->PantRear( true ); + if( ( mvControlling->PantRearVolt == 0.0 ) + // filter out cases with single _other_ working pantograph so we don't try to raise something we can't + && ( ( mvControlling->PantographVoltage == 0.0 ) + || ( mvControlling->EnginePowerSource.CollectorParameters.CollectorsNo > 1 ) ) ) { + mvControlling->OperatePantographValve( end::rear, operation_t::enable ); + } } else { - mvControlling->PantFront( true ); + // jak jedzie w kierunku sprzęgu 0 + if( ( mvControlling->PantFrontVolt == 0.0 ) + // filter out cases with single _other_ working pantograph so we don't try to raise something we can't + && ( ( mvControlling->PantographVoltage == 0.0 ) + || ( mvControlling->EnginePowerSource.CollectorParameters.CollectorsNo > 1 ) ) ) { + mvControlling->OperatePantographValve( end::front, operation_t::enable ); + } } } if( mvOccupied->Vel > 5 ) { // opuszczenie przedniego po rozpędzeniu się o ile jest więcej niż jeden if( mvControlling->EnginePowerSource.CollectorParameters.CollectorsNo > 1 ) { - if( iDirection >= 0 ) // jak jedzie w kierunku sprzęgu 0 + if( ( iDirection >= 0 ) && ( useregularpantographlayout ) ) // jak jedzie w kierunku sprzęgu 0 { // poczekać na podniesienie tylnego - if( mvControlling->PantRearVolt != 0.0 ) { - // czy jest napięcie zasilające na tylnym? - mvControlling->PantFront( false ); // opuszcza od sprzęgu 0 + if( ( mvControlling->PantFrontVolt != 0.0 ) + && ( mvControlling->PantRearVolt != 0.0 ) ) { // czy jest napięcie zasilające na tylnym? + mvControlling->OperatePantographValve( end::front, operation_t::disable ); // opuszcza od sprzęgu 0 } } else { // poczekać na podniesienie przedniego - if( mvControlling->PantFrontVolt != 0.0 ) { - // czy jest napięcie zasilające na przednim? - mvControlling->PantRear( false ); // opuszcza od sprzęgu 1 + if( ( mvControlling->PantRearVolt != 0.0 ) + && ( mvControlling->PantFrontVolt != 0.0 ) ) { // czy jest napięcie zasilające na przednim? + mvControlling->OperatePantographValve( end::rear, operation_t::disable ); // opuszcza od sprzęgu 1 } } } @@ -4761,10 +4779,17 @@ TController::UpdateSituation(double dt) { // NOTE: abs(stoptime) covers either at least 15 sec remaining for a scheduled stop, or 15+ secs spent at a basic stop && ( std::abs( fStopTime ) > 15.0 ) ) { // spending a longer at a stop, raise also front pantograph - if( iDirection >= 0 ) // jak jedzie w kierunku sprzęgu 0 - mvControlling->PantFront( true ); - else - mvControlling->PantRear( true ); + if( ( iDirection >= 0 ) && ( useregularpantographlayout ) ) { + // jak jedzie w kierunku sprzęgu 0 + if( mvControlling->PantFrontVolt == 0.0 ) { + mvControlling->OperatePantographValve( end::front, operation_t::enable ); + } + } + else { + if( mvControlling->PantRearVolt == 0.0 ) { + mvControlling->OperatePantographValve( end::rear, operation_t::enable ); + } + } } } } @@ -7004,7 +7029,7 @@ void TController::DirectionForward(bool forward) { // ustawienie jazdy do przodu dla true i do tyłu dla false (zależy od kabiny) ZeroSpeed( true ); // TODO: check if force switch is needed anymore here // HACK: make sure the master controller isn't set in position which prevents direction change - mvControlling->MainCtrlPos = std::min( mvControlling->MainCtrlPos, mvControlling->MaxMainCtrlPosNoDirChange ); + mvControlling->MainCtrlPos = std::min( mvControlling->MainCtrlPos, mvControlling->MainCtrlMaxDirChangePos ); if( forward ) { // do przodu w obecnej kabinie diff --git a/DynObj.cpp b/DynObj.cpp index 8b15d1f3..ba47e876 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -2788,8 +2788,8 @@ bool TDynamicObject::Update(double dt, double dt1) if( MoverParameters->Vel > 0.5 ) { // jeśli jedzie // Ra 2014-07: doraźna blokada logowania zimnych lokomotyw - zrobić to trzeba inaczej - if( MoverParameters->PantFrontUp - || MoverParameters->PantRearUp ) { + if( MoverParameters->Pantographs[end::front].is_active + || MoverParameters->Pantographs[end::rear].is_active ) { if( ( MoverParameters->Mains ) && ( MoverParameters->GetAnyTrainsetVoltage() < 0.1f ) ) { @@ -3380,7 +3380,7 @@ bool TDynamicObject::Update(double dt, double dt1) 0.95 * MoverParameters->EnginePowerSource.MaxVoltage : 0.0; } - else if( ( true == MoverParameters->PantFrontUp ) + else if( ( true == MoverParameters->Pantographs[ end::front ].is_active ) && ( PantDiff < 0.01 ) ) // tolerancja niedolegania { if (p->hvPowerWire) { @@ -3422,7 +3422,7 @@ bool TDynamicObject::Update(double dt, double dt1) 0.95 * MoverParameters->EnginePowerSource.MaxVoltage : 0.0; } - else if ( ( true == MoverParameters->PantRearUp ) + else if ( ( true == MoverParameters->Pantographs[ end::rear ].is_active ) && ( PantDiff < 0.01 ) ) { if (p->hvPowerWire) { @@ -3476,9 +3476,7 @@ bool TDynamicObject::Update(double dt, double dt1) pantspeedfactor = std::max( 0.0, pantspeedfactor ); k = p->fAngleL; if( ( pantspeedfactor > 0.0 ) - && ( i ? - MoverParameters->PantRearUp : - MoverParameters->PantFrontUp ) )// jeśli ma być podniesiony + && ( MoverParameters->Pantographs[i].is_active ) )// jeśli ma być podniesiony { if (PantDiff > 0.001) // jeśli nie dolega do drutu { // jeśli poprzednia wysokość jest mniejsza niż pożądana, zwiększyć kąt dolnego @@ -3522,39 +3520,39 @@ bool TDynamicObject::Update(double dt, double dt1) } } } // koniec pętli po pantografach - if ((MoverParameters->PantFrontSP == false) && (MoverParameters->PantFrontUp == false)) - { - for( auto &pantograph : m_pantographsounds ) { - if( pantograph.sPantDown.offset().z > 0 ) { - // limit to pantographs located in the front half of the vehicle - pantograph.sPantDown.play( sound_flags::exclusive ); + // TBD, TODO: generate sound event during mover update instead? + if( MoverParameters->Pantographs[end::front].sound_event != MoverParameters->Pantographs[ end::front ].is_active ) { + if( MoverParameters->Pantographs[ end::front ].is_active ) { + // pantograph moving up + // TBD: add a sound? + } + else { + // pantograph dropping + for( auto &pantograph : m_pantographsounds ) { + if( pantograph.sPantDown.offset().z > 0 ) { + // limit to pantographs located in the front half of the vehicle + pantograph.sPantDown.play( sound_flags::exclusive ); + } } } - MoverParameters->PantFrontSP = true; + MoverParameters->Pantographs[ end::front ].sound_event = MoverParameters->Pantographs[ end::front ].is_active; } - if ((MoverParameters->PantRearSP == false) && (MoverParameters->PantRearUp == false)) - { - for( auto &pantograph : m_pantographsounds ) { - if( pantograph.sPantDown.offset().z < 0 ) { - // limit to pantographs located in the rear half of the vehicle - pantograph.sPantDown.play( sound_flags::exclusive ); + if( MoverParameters->Pantographs[ end::rear ].sound_event != MoverParameters->Pantographs[ end::rear ].is_active ) { + if( MoverParameters->Pantographs[ end::rear ].is_active ) { + // pantograph moving up + // TBD: add a sound? + } + else { + // pantograph dropping + for( auto &pantograph : m_pantographsounds ) { + if( pantograph.sPantDown.offset().z < 0 ) { + // limit to pantographs located in the front half of the vehicle + pantograph.sPantDown.play( sound_flags::exclusive ); + } } } - MoverParameters->PantRearSP = true; + MoverParameters->Pantographs[ end::rear ].sound_event = MoverParameters->Pantographs[ end::rear ].is_active; } -/* - // NOTE: disabled because it's both redundant and doesn't take into account alternative power sources - // converter and compressor will (should) turn off during their individual checks, in the mover's (fast)computemovement() calls - if (MoverParameters->EnginePowerSource.SourceType == CurrentCollector) - { // Winger 240404 - wylaczanie sprezarki i - // przetwornicy przy braku napiecia - if (tmpTraction.TractionVoltage == 0) - { // to coś wyłączało dźwięk silnika w ST43! - MoverParameters->ConverterFlag = false; - MoverParameters->CompressorFlag = false; // Ra: to jest wątpliwe - wyłączenie sprężarki powinno być w jednym miejscu! - } - } -*/ } else if (MoverParameters->EnginePowerSource.SourceType == TPowerSource::InternalSource) if (MoverParameters->EnginePowerSource.PowerType == TPowerType::SteamPower) @@ -3887,6 +3885,25 @@ void TDynamicObject::RenderSounds() { } } + // emergency brake + if( MoverParameters->EmergencyValveFlow > 0.025 ) { + // smooth out air flow rate + m_emergencybrakeflow = ( + m_emergencybrakeflow == 0.0 ? + MoverParameters->EmergencyValveFlow : + interpolate( m_emergencybrakeflow, MoverParameters->EmergencyValveFlow, 0.1 ) ); + // scale volume based on the flow rate and on the pressure in the main pipe + auto const flowpressure { clamp( m_emergencybrakeflow, 0.0, 1.0 ) + clamp( 0.1 * MoverParameters->PipePress, 0.0, 0.5 ) }; + m_emergencybrake + .pitch( m_emergencybrake.m_frequencyoffset + 1.0 * m_emergencybrake.m_frequencyfactor ) + .gain( m_emergencybrake.m_amplitudeoffset + clamp( flowpressure, 0.0, 1.0 ) * m_emergencybrake.m_amplitudefactor ) + .play( sound_flags::exclusive | sound_flags::looping ); + } + else if( MoverParameters->EmergencyValveFlow < 0.015 ) { + m_emergencybrakeflow = 0.0; + m_emergencybrake.stop(); + } + // air release if( m_lastbrakepressure != -1.f ) { // calculate rate of pressure drop in brake cylinder, once it's been initialized @@ -5251,6 +5268,12 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co m_brakecylinderpistonrecede.owner( this ); } + else if( token == "emergencybrake:" ) { + // emergency brake sound + m_emergencybrake.deserialize( parser, sound_type::single ); + m_emergencybrake.owner( this ); + } + else if( token == "brakeacc:" ) { // plik z przyspieszaczem (upust po zlapaniu hamowania) sBrakeAcc.deserialize( parser, sound_type::single ); @@ -7193,11 +7216,8 @@ vehicle_table::update_traction( TDynamicObject *Vehicle ) { for( int pantographindex = 0; pantographindex < Vehicle->iAnimType[ ANIM_PANTS ]; ++pantographindex ) { // pętla po pantografach - auto pantograph { Vehicle->pants[ pantographindex ].fParamPants }; - if( true == ( - pantographindex == end::front ? - Vehicle->MoverParameters->PantFrontUp : - Vehicle->MoverParameters->PantRearUp ) ) { + auto *pantograph { Vehicle->pants[ pantographindex ].fParamPants }; + if( true == Vehicle->MoverParameters->Pantographs[ pantographindex ].is_active ) { // jeśli pantograf podniesiony auto const pant0 { position + ( vLeft * pantograph->vPos.z ) + ( vUp * pantograph->vPos.y ) + ( vFront * pantograph->vPos.x ) }; if( pantograph->hvPowerWire != nullptr ) { diff --git a/DynObj.h b/DynObj.h index 602d0b42..b61d9620 100644 --- a/DynObj.h +++ b/DynObj.h @@ -447,6 +447,8 @@ private: sound_source m_brakecylinderpistonrecede { sound_placement::external }; float m_lastbrakepressure { -1.f }; // helper, cached level of pressure in the brake cylinder float m_brakepressurechange { 0.f }; // recent change of pressure in the brake cylinder + sound_source m_emergencybrake { sound_placement::engine }; + double m_emergencybrakeflow{ 0.f }; sound_source sReleaser { sound_placement::external }; sound_source rsSlippery { sound_placement::external, EU07_SOUND_BRAKINGCUTOFFRANGE }; // moved from cab sound_source sSand { sound_placement::external }; diff --git a/Gauge.h b/Gauge.h index a215227d..86b65120 100644 --- a/Gauge.h +++ b/Gauge.h @@ -23,7 +23,7 @@ enum class TGaugeAnimation { enum class TGaugeType : int { toggle = 1, - push, + push = 2, pushtoggle = ( toggle | push ), push_delayed }; diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index e9781c21..b9bb4ae1 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -168,6 +168,16 @@ enum class range_t { unit, consist }; +// possible settings of enable/disable input pair; exclusive +enum class operation_t { + enable_on = 1, + enable_off = -1, + disable_on = 2, + disable_off = -2, + none = 0, + enable, + disable, +}; // start method for devices; exclusive enum class start_t { manual, @@ -778,6 +788,23 @@ private: bool breaker { true }; // device is allowed to operate }; + // basic approximation of a solenoid valve + struct basic_valve : basic_device { + // config + bool solenoid { true }; // requires electric power to operate + bool spring { true }; // spring return or double acting actuator + }; + + // basic approximation of a pantograph + struct basic_pantograph { + // ld inputs + basic_valve valve; // associated pneumatic valve + // ld outputs + bool is_active { false }; // device is working + bool sound_event { false }; // indicates last state which generated sound event + double voltage { 0.0 }; + }; + // basic approximation of doors struct basic_door { // config @@ -1085,8 +1112,7 @@ public: int FastSerialCircuit = 0;/*0 - po kolei zamyka styczniki az do osiagniecia szeregowej, 1 - natychmiastowe wejscie na szeregowa*/ /*hunter-111012*/ int AutoRelayType = 0; /*0 -brak, 1 - jest, 2 - opcja*/ bool CoupledCtrl = false; /*czy mainctrl i scndctrl sa sprzezone*/ - //CouplerNr: TCouplerNr; {ABu: nr sprzegu podlaczonego w drugim obiekcie} - bool IsCoupled = false; /*czy jest sprzezony ale jedzie z tylu*/ + bool HasCamshaft { false }; int DynamicBrakeType = 0; /*patrz dbrake_**/ int DynamicBrakeAmpmeters = 2; /*liczba amperomierzy przy hamowaniu ED*/ double DynamicBrakeRes = 5.8; /*rezystancja oporników przy hamowaniu ED*/ @@ -1265,7 +1291,11 @@ public: bool SandDoseAutoAllow = true; /*zezwolenie na automatyczne piaskowanie*/ double Sand = 0.0; /*ilosc piasku*/ double BrakeSlippingTimer = 0.0; /*pomocnicza zmienna do wylaczania przeciwposlizgu*/ - double dpBrake = 0.0; double dpPipe = 0.0; double dpMainValve = 0.0; double dpLocalValve = 0.0; + double dpBrake = 0.0; + double dpPipe = 0.0; + double dpMainValve = 0.0; + double dpLocalValve = 0.0; + double EmergencyValveFlow = 0.0; // air flow through alerter valve during last simulation step /*! przyrosty cisnienia w kroku czasowym*/ double ScndPipePress = 0.0; /*cisnienie w przewodzie zasilajacym*/ double BrakePress = 0.0; /*!o cisnienie w cylindrach hamulcowych*/ @@ -1356,7 +1386,7 @@ public: int CompressorListPos = 0; /*polozenie przelacznika wielopozycyjnego sprezarek*/ int DirActive = 0; //czy lok. jest wlaczona i w ktorym kierunku: względem wybranej kabiny: -1 - do tylu, +1 - do przodu, 0 - wylaczona int DirAbsolute = 0; //zadany kierunek jazdy względem sprzęgów (1=w strone 0,-1=w stronę 1) - int MaxMainCtrlPosNoDirChange { 0 }; // can't change reverser state with master controller set above this position + int MainCtrlMaxDirChangePos { 0 }; // can't change reverser state with master controller set above this position int CabActive = 0; //numer kabiny, z której jest sterowanie: 1 lub -1; w przeciwnym razie brak sterowania - rozrzad int CabOccupied = 0; //numer kabiny, w ktorej jest obsada (zwykle jedna na skład) // TODO: move to TController double LastSwitchingTime = 0.0; /*czas ostatniego przelaczania czegos*/ @@ -1474,10 +1504,9 @@ public: bool DoorRightOpened = false; double DoorRightOpenTimer{ -1.0 }; // right door closing timer for automatic door type #endif - bool PantFrontUp = false; //stan patykow 'Winger 160204 - bool PantRearUp = false; - bool PantFrontSP = true; //dzwiek patykow 'Winger 010304 - bool PantRearSP = true; + basic_valve PantsValve; + std::array Pantographs; + bool PantAllDown { false }; double PantFrontVolt = 0.0; //pantograf pod napieciem? 'Winger 160404 double PantRearVolt = 0.0; // TODO: move these switch types where they belong, cabin definition @@ -1515,6 +1544,7 @@ public: void derail( int const Reason ); bool DirectionForward(); bool DirectionBackward( void );/*! kierunek ruchu*/ + bool EIMDirectionChangeAllow( void ) const; void BrakeLevelSet(double b); bool BrakeLevelAdd(double b); bool IncBrakeLevel(); // wersja na użytek AI @@ -1552,8 +1582,10 @@ public: /*! glowny nastawnik:*/ bool IncMainCtrl(int CtrlSpeed); bool DecMainCtrl(int CtrlSpeed); + bool IsMainCtrlActualNoPowerPos() const; // whether the master controller is actually set to position which won't generate any extra power bool IsMainCtrlNoPowerPos() const; // whether the master controller is set to position which won't generate any extra power int MainCtrlNoPowerPos() const; // highest setting of master controller which won't cause engine to generate extra power + int MainCtrlActualPowerPos() const; // current actual setting of master controller, relative to the highest setting not generating extra power int MainCtrlPowerPos() const; // current setting of master controller, relative to the highest setting not generating extra power /*! pomocniczy nastawnik:*/ bool IncScndCtrl(int CtrlSpeed); @@ -1654,6 +1686,7 @@ public: void FuelPumpCheck( double const Timestep ); void OilPumpCheck( double const Timestep ); void MotorBlowersCheck( double const Timestep ); + void PantographsCheck( double const Timestep ); bool FuseOn(void); //bezpiecznik nadamiary bool FuseFlagCheck(void) const; // sprawdzanie flagi nadmiarowego void FuseOff(void); // wylaczenie nadmiarowego @@ -1675,10 +1708,11 @@ public: bool AutoRelaySwitch(bool State); //przelacznik automatycznego rozruchu bool AutoRelayCheck();//symulacja automatycznego rozruchu bool MotorConnectorsCheck(); - bool ResistorsFlagCheck(void) const; //sprawdzenie kontrolki oporow rozruchowych NBMX - bool PantFront( bool const State, range_t const Notify = range_t::consist ); //obsluga pantografou przedniego - bool PantRear( bool const State, range_t const Notify = range_t::consist ); //obsluga pantografu tylnego + + bool OperatePantographsValve( operation_t const State, range_t const Notify = range_t::consist ); + bool OperatePantographValve( end const End, operation_t const State, range_t const Notify = range_t::consist ); + bool DropAllPantographs( bool const State, range_t const Notify = range_t::consist ); void CheckEIMIC(double dt); //sprawdzenie i zmiana nastawy zintegrowanego nastawnika jazdy/hamowania void CheckSpeedCtrl(double dt); @@ -1768,7 +1802,6 @@ private: bool readCompressorList(std::string const &Input); void BrakeValveDecode( std::string const &s ); //Q 20160719 void BrakeSubsystemDecode(); //Q 20160719 - bool EIMDirectionChangeAllow( void ); }; //double Distance(TLocation Loc1, TLocation Loc2, TDimension Dim1, TDimension Dim2); diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index a0b19e78..583fe5e9 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -531,7 +531,9 @@ bool TMoverParameters::Dettach(int ConnectNo) bool TMoverParameters::DirectionForward() { - if ((MainCtrlPosNo > 0) && (DirActive < 1) && (EIMDirectionChangeAllow())) + if( false == EIMDirectionChangeAllow() ) { return false; } + + if ((MainCtrlPosNo > 0) && (DirActive < 1)) { ++DirActive; DirAbsolute = DirActive * CabActive; @@ -678,23 +680,19 @@ TMoverParameters::CurrentSwitch(bool const State) { return false; }; -void TMoverParameters::UpdatePantVolume(double dt) -{ // KURS90 - sprężarka pantografów; Ra 2014-07: teraz jest to zbiornik rozrządu, chociaż to jeszcze nie tak - + // KURS90 - sprężarka pantografów; Ra 2014-07: teraz jest to zbiornik rozrządu, chociaż to jeszcze nie tak +void TMoverParameters::UpdatePantVolume(double dt) { // check the pantograph compressor while at it - if( ( PantPress < 4.2 ) + // TODO: move the check to a separate method + // automatic start if the pressure is too low + PantCompFlag |= ( + ( PantPress < 4.2 ) + && ( true == ( Pantographs[ end::front ].is_active | Pantographs[ end::rear ].is_active ) ) // TODO: any_pantograph_is_active method && ( ( PantographCompressorStart == start_t::automatic ) - || ( PantographCompressorStart == start_t::manualwithautofallback ) ) - && ( ( true == PantRearUp ) - || ( true == PantFrontUp ) ) ) { - // automatic start if the pressure is too low - PantCompFlag = true; - } - if( ( true == PantCompFlag ) - && ( false == Battery ) - && ( false == ConverterFlag ) ) { - PantCompFlag = false; - } + || ( PantographCompressorStart == start_t::manualwithautofallback ) ) ); + + auto const lowvoltagepower { Battery | ConverterFlag }; + PantCompFlag &= lowvoltagepower; if (EnginePowerSource.SourceType == TPowerSource::CurrentCollector) // tylko jeśli pantografujący { @@ -768,20 +766,6 @@ void TMoverParameters::UpdatePantVolume(double dt) } } } -/* - // NOTE: pantograph tank pressure sharing experimentally disabled for more accurate simulation - if (TrainType != dt_EZT) // w EN57 pompuje się tylko w silnikowym - // pierwotnie w CHK pantografy miały również rozrządcze EZT - for (int b = 0; b <= 1; ++b) - if (TestFlag(Couplers[b].CouplingFlag, ctrain_controll)) - if (Couplers[b].Connected->PantVolume < - PantVolume) // bo inaczej trzeba w obydwu członach przestawiać - Couplers[b].Connected->PantVolume = - PantVolume; // przekazanie ciśnienia do sąsiedniego członu - // czy np. w ET40, ET41, ET42 pantografy członów mają połączenie pneumatyczne? - // Ra 2014-07: raczej nie - najpierw się załącza jeden człon, a potem można podnieść w - // drugim -*/ } else { // a tu coś dla SM42 i SM31, aby pokazywać na manometrze @@ -1430,6 +1414,8 @@ void TMoverParameters::compute_movement_( double const Deltatime ) { // TODO: gather and move current calculations to dedicated method TotalCurrent = 0; + // power sources + PantographsCheck( Deltatime ); // main circuit MainsCheck( Deltatime ); // traction motors @@ -1849,7 +1835,9 @@ void TMoverParameters::MotorBlowersCheck( double const Timestep ) { // && ( true == blower.breaker ) && ( false == blower.is_disabled ) && ( ( true == blower.is_active ) - || ( blower.start_type == start_t::manual ? blower.is_enabled : true ) ) ); + || ( blower.start_type == start_t::manual ? + blower.is_enabled : + true ) ) ); } // update for( auto &fan : MotorBlowers ) { @@ -1873,6 +1861,46 @@ void TMoverParameters::MotorBlowersCheck( double const Timestep ) { } } +void TMoverParameters::PantographsCheck( double const Timestep ) { + + { + auto &valve { PantsValve }; + auto const lowvoltagepower{ valve.solenoid ? ( Battery || ConverterFlag ) : true }; + auto const autostart{ valve.start_type == start_t::automatic || valve.start_type == start_t::manualwithautofallback }; + auto const manualcontrol{ valve.start_type == start_t::manual || valve.start_type == start_t::manualwithautofallback }; + + PantsValve.is_active = ( + ( ( valve.spring ? lowvoltagepower : true ) ) // spring actuator needs power to maintain non-default state + && ( ( ( manualcontrol && lowvoltagepower ) ? false == valve.is_disabled : true ) ) // needs power to change state + && ( ( valve.is_active ) + || ( autostart ? lowvoltagepower : + !autostart ? ( lowvoltagepower && valve.is_enabled ) : + false ) ) ); // shouldn't ever get this far but, eh + } + + for( auto &pantograph : Pantographs ) { + + auto &valve { pantograph.valve }; + auto const lowvoltagepower { valve.solenoid ? ( Battery || ConverterFlag ) : true }; + auto const autostart { valve.start_type == start_t::automatic || valve.start_type == start_t::manualwithautofallback }; + auto const manualcontrol { valve.start_type == start_t::manual || valve.start_type == start_t::manualwithautofallback }; + + valve.is_active = ( + ( ( valve.spring ? lowvoltagepower : true ) ) // spring actuator needs power to maintain non-default state + && ( ( ( manualcontrol && lowvoltagepower ) ? false == valve.is_disabled : true ) ) // needs power to change state + && ( ( ( manualcontrol && lowvoltagepower ) ? false == PantAllDown : true ) ) + && ( ( valve.is_active ) + || ( autostart ? lowvoltagepower : + !autostart ? ( lowvoltagepower && valve.is_enabled ) : + false ) ) ); // shouldn't ever get this far but, eh + + pantograph.is_active = ( + ( valve.is_active ) + && ( PantsValve.is_active ) +// && ( ) // TODO: add other checks + ); + } +} double TMoverParameters::ShowCurrent(int AmpN) const { // Odczyt poboru prądu na podanym amperomierzu @@ -2184,6 +2212,11 @@ bool TMoverParameters::DecMainCtrl(int CtrlSpeed) return OK; } +bool TMoverParameters::IsMainCtrlActualNoPowerPos() const { + // TODO: wrap controller pieces into a class for potential specializations, similar to brake subsystems + return MainCtrlActualPos <= MainCtrlNoPowerPos(); +} + bool TMoverParameters::IsMainCtrlNoPowerPos() const { // TODO: wrap controller pieces into a class for potential specializations, similar to brake subsystems return MainCtrlPos <= MainCtrlNoPowerPos(); @@ -2199,6 +2232,11 @@ int TMoverParameters::MainCtrlNoPowerPos() const { } } +int TMoverParameters::MainCtrlActualPowerPos() const { + + return MainCtrlActualPos - MainCtrlNoPowerPos(); +} + int TMoverParameters::MainCtrlPowerPos() const { return MainCtrlPos - MainCtrlNoPowerPos(); @@ -2765,39 +2803,39 @@ bool TMoverParameters::SpringBrakeRelease() // ************************************************************************************************* bool TMoverParameters::DirectionBackward(void) { - bool DB = false; + if( false == EIMDirectionChangeAllow() ) { return false; } + if ((DirActive == 1) && (MainCtrlPos == 0) && (TrainType == dt_EZT) && (EngineType != TEngineType::ElectricInductionMotor)) if (MinCurrentSwitch(false)) { - DB = true; // - return DB; // exit; TODO: czy dobrze przetlumaczone? + return true; } - if ((MainCtrlPosNo > 0) && (DirActive > -1) && (EIMDirectionChangeAllow())) + if ((MainCtrlPosNo > 0) && (DirActive > -1)) { if (EngineType == TEngineType::WheelsDriven) - CabActive--; + --CabActive; // else - DirActive--; + --DirActive; DirAbsolute = DirActive * CabActive; + // TODO: move shp activation to shp check if (DirAbsolute != 0) if (Battery) // jeśli bateria jest już załączona BatterySwitch(true); // to w ten oto durny sposób aktywuje się CA/SHP - DB = true; SendCtrlToNext("Direction", DirActive, CabActive); + return true; } - else - DB = false; - return DB; + + return false; } -bool TMoverParameters::EIMDirectionChangeAllow(void) +bool TMoverParameters::EIMDirectionChangeAllow(void) const { bool OK = false; /* // NOTE: disabled while eimic variables aren't immediately synced with master controller changes inside ai module OK = (EngineType != TEngineType::ElectricInductionMotor || ((eimic <= 0) && (eimic_real <= 0) && (Vel < 0.1))); */ - OK = ( MainCtrlPos <= MaxMainCtrlPosNoDirChange ); + OK = ( MainCtrlPos <= MainCtrlMaxDirChangePos ); return OK; } @@ -3936,6 +3974,8 @@ void TMoverParameters::UpdatePipePressure(double dt) } // ulepszony hamulec bezp. + EmergencyValveFlow = 0.0; + auto const securitysystempresent { SecuritySystem.RadioStop || ( SecuritySystem.SystemType > 0 ) }; auto const lowvoltagepower { Battery || ConverterFlag }; @@ -3951,8 +3991,10 @@ void TMoverParameters::UpdatePipePressure(double dt) || ( true == s_CAtestebrake ) || ( ( true == securitysystempresent ) && ( false == lowvoltagepower ) ) ) { - dpMainValve += PF( 0, PipePress, 0.15 ) * dt; + EmergencyValveFlow = PF( 0, PipePress, 0.15 ) * dt; } + dpMainValve += EmergencyValveFlow; + // 0.2*Spg Pipe->Flow(-dpMainValve); Pipe->Flow(-(PipePress)*0.001 * dt); @@ -5045,6 +5087,8 @@ double TMoverParameters::TractionForce( double dt ) { case TEngineType::DieselElectric: { // TODO: move this to the auto relay check when the electric engine code paths are unified StLinFlag = MotorConnectorsCheck(); + StLinFlag &= IsMainCtrlNoPowerPos(); + break; } @@ -6105,7 +6149,8 @@ bool TMoverParameters::AutoRelayCheck(void) // Ra 2014-06: dla SN61 nie działa prawidłowo // yBARC - rozlaczenie stycznikow liniowych - if( false == motorconnectors ) { + if( ( false == motorconnectors ) + || ( HasCamshaft ? IsMainCtrlActualNoPowerPos() : IsMainCtrlNoPowerPos() ) ) { StLinFlag = false; OK = false; if( false == DynamicBrakeFlag ) { @@ -6301,7 +6346,7 @@ bool TMoverParameters::AutoRelayCheck(void) && ( ( MainCtrlActualPos > 0 ) || ( ScndCtrlActualPos > 0 ) ) ) { - if( true == CoupledCtrl ) { + if( CoupledCtrl ) { if( TrainType == dt_EZT ) { // EN57 wal jednokierunkowy calosciowy @@ -6374,7 +6419,6 @@ bool TMoverParameters::MotorConnectorsCheck() { ( false == Mains ) || ( true == FuseFlag ) || ( true == StLinSwitchOff ) - || ( IsMainCtrlNoPowerPos() ) || ( DirActive == 0 ) }; if( connectorsoff ) { return false; } @@ -6389,106 +6433,70 @@ bool TMoverParameters::MotorConnectorsCheck() { return connectorson; } -// ************************************************************************************************* -// Q: 20160713 -// Podnosi / opuszcza przedni pantograf. Returns: state of the pantograph after the operation -// ************************************************************************************************* -bool TMoverParameters::PantFront( bool const State, range_t const Notify ) -{ -/* - if( ( true == Battery ) - || ( true == ConverterFlag ) ) { -*/ - if( PantFrontUp != State ) { - PantFrontUp = State; - if( State == true ) { - if( Notify != range_t::local ) { - // wysłanie wyłączenia do pozostałych? - SendCtrlToNext( - "PantFront", 1, CabActive, - ( Notify == range_t::unit ? - ctrain_controll | ctrain_depot : - ctrain_controll ) ); - } - } - else { - if( Notify != range_t::local ) { - // wysłanie wyłączenia do pozostałych? - SendCtrlToNext( - "PantFront", 0, CabActive, - ( Notify == range_t::unit ? - ctrain_controll | ctrain_depot : - ctrain_controll ) ); - } - } - } -/* +bool TMoverParameters::OperatePantographsValve( operation_t const State, range_t const Notify ) { + + auto const lowvoltagepower { PantsValve.solenoid ? ( Battery || ConverterFlag ) : true }; + + auto &valve { PantsValve }; + + valve.is_enabled = ( State == operation_t::enable ); + valve.is_disabled = ( State == operation_t::disable ); + + if( Notify != range_t::local ) { + SendCtrlToNext( + "PantsValve", + static_cast( State ), + CabActive, + ( Notify == range_t::unit ? + coupling::control | coupling::permanent : + coupling::control ) ); } - else { - // no power, drop the pantograph - // NOTE: this is a simplification as it should just drop on its own with loss of pressure without resupply from (dead) compressor - PantFrontStart = ( - PantFrontUp ? - 1 : - 0 ); - PantFrontUp = false; - if( true == Multiunitcontrol ) { - SendCtrlToNext( "PantFront", 0, CabActive ); - } - } -*/ - return PantFrontUp; + + return true; } -// ************************************************************************************************* -// Q: 20160713 -// Podnoszenie / opuszczanie pantografu tylnego -// ************************************************************************************************* -bool TMoverParameters::PantRear( bool const State, range_t const Notify ) -{ -/* - if( ( true == Battery ) - || ( true == ConverterFlag ) ) { -*/ - if( PantRearUp != State ) { - PantRearUp = State; - if( State == true ) { - if( Notify != range_t::local ) { - // wysłanie wyłączenia do pozostałych? - SendCtrlToNext( - "PantRear", 1, CabActive, - ( Notify == range_t::unit ? - ctrain_controll | ctrain_depot : - ctrain_controll ) ); - } - } - else { - if( Notify != range_t::local ) { - // wysłanie wyłączenia do pozostałych? - SendCtrlToNext( - "PantRear", 0, CabActive, - ( Notify == range_t::unit ? - ctrain_controll | ctrain_depot : - ctrain_controll ) ); - } - } - } -/* +bool TMoverParameters::OperatePantographValve( end const End, operation_t const State, range_t const Notify ) { + + auto &valve { Pantographs[ End ].valve }; + + valve.is_enabled = ( State == operation_t::enable ); + valve.is_disabled = ( State == operation_t::disable ); + + if( Notify != range_t::local ) { + SendCtrlToNext( + "PantValve", + // HACK: pack the state, pantograph index and sender cab into 8-bit value + // with high bit storing front/rear pantograph, and 7th bit storing sender cab + static_cast( + 0x80 * ( End == end::front ? 0 : 1 ) + + 0x40 * ( CabActive != -1 ? 1 : 0 ) + + static_cast( State ) ), + CabActive, + ( Notify == range_t::unit ? + coupling::control | coupling::permanent : + coupling::control ) ); } - else { - // no power, drop the pantograph - // NOTE: this is a simplification as it should just drop on its own with loss of pressure without resupply from (dead) compressor - PantRearStart = ( - PantRearUp ? - 1 : - 0 ); - PantRearUp = false; - if( true == Multiunitcontrol ) { - SendCtrlToNext( "PantRear", 0, CabActive ); - } + + return true; +} + +bool TMoverParameters::DropAllPantographs( bool const State, range_t const Notify ) { + + auto const initialstate{ PantAllDown }; + + PantAllDown = State; + + if( Notify != range_t::local ) { + SendCtrlToNext( + "PantAllDown", + ( State ? 1 : 0 ), + CabActive, + ( Notify == range_t::unit ? + coupling::control | coupling::permanent : + coupling::control ) ); } -*/ - return PantRearUp; + + return State != initialstate; } void TMoverParameters::CheckEIMIC(double dt) @@ -7509,12 +7517,12 @@ TMoverParameters::AssignLoad( std::string const &Name, float const Amount ) { DoubleTr = -1; } if( pantographsetup & ( 1 << 0 ) ) { - if( DoubleTr == 1 ) { PantFront( true ); } - else { PantRear( true ); } + if( DoubleTr == 1 ) { OperatePantographValve( end::front, operation_t::enable, range_t::local ); } + else { OperatePantographValve( end::rear, operation_t::enable, range_t::local ); } } if( pantographsetup & ( 1 << 1 ) ) { - if( DoubleTr == 1 ) { PantRear( true ); } - else { PantFront( true ); } + if( DoubleTr == 1 ) { OperatePantographValve( end::rear, operation_t::enable, range_t::local ); } + else { OperatePantographValve( end::front, operation_t::enable, range_t::local ); } } return true; } @@ -9586,7 +9594,7 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) { extract_value( MainCtrlPosNo, "MCPN", line, "" ); extract_value( ScndCtrlPosNo, "SCPN", line, "" ); extract_value( ScndInMain, "SCIM", line, "" ); - extract_value( MaxMainCtrlPosNoDirChange, "DirChangeMaxPos", line, "" ); + extract_value( MainCtrlMaxDirChangePos, "DirChangeMaxPos", line, "" ); auto const autorelay { ToLower( extract_value( "AutoRelay", line ) ) }; if( autorelay == "optional" ) { AutoRelayType = 2; } @@ -9594,7 +9602,8 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) { else { AutoRelayType = 0; } extract_value( CoupledCtrl, "CoupledCtrl", line, "" ); - extract_value( EIMCtrlType, "EIMCtrlType", line, "" ); + extract_value( HasCamshaft, "Camshaft", line, "" ); + extract_value( EIMCtrlType, "EIMCtrlType", line, "" ); EIMCtrlType = clamp( EIMCtrlType, 0, 3 ); extract_value( LocHandleTimeTraxx, "LocalBrakeTraxx", line, "" ); extract_value( EIMCtrlAdditionalZeros, "EIMCtrlAddZeros", line, "" ); @@ -9656,6 +9665,32 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) { // pantograph compressor valve PantAutoValve = ( TrainType == dt_EZT ); // legacy code behaviour, automatic valve was initially installed in all EMUs extract_value( PantAutoValve, "PantAutoValve", line, "" ); + // pantographs valve + { + auto lookup = starts.find( extract_value( "PantEPValveStart", line ) ); + PantsValve.start_type = + lookup != starts.end() ? + lookup->second : + start_t::automatic; // legacy code behaviour, there was no pantographs valve + extract_value( PantsValve.spring, "PantEPValveSpring", line, "" ); + } + // pantograph valve configuration + { + auto lookup = starts.find( extract_value( "PantValveStart", line ) ); + auto valvestarttype = + lookup != starts.end() ? + lookup->second : + start_t::manual; + auto valvespring { true }; + extract_value( valvespring, "PantValveSpring", line, "" ); + auto valvesolenoid { true }; + extract_value( valvesolenoid, "PantValveSolenoid", line, "" ); + for( auto &pantograph : Pantographs ) { + pantograph.valve.spring = valvespring; + pantograph.valve.solenoid = valvesolenoid; + pantograph.valve.start_type = valvestarttype; + } + } // fuel pump { auto lookup = starts.find( extract_value( "FuelStart", line ) ); @@ -11146,86 +11181,31 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C false ); OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); } - else if (Command == "PantFront") /*Winger 160204*/ + else if (Command == "PantValve") //Winger 160204 { // Ra: uwzględnić trzeba jeszcze zgodność sprzęgów - // Czemu EZT ma być traktowane inaczej? Ukrotnienie ma, a człon może być odwrócony - if ((TrainType == dt_EZT) - || (TrainType == dt_ET41)) - { //'ezt' - if ((CValue1 == 1)) - { - PantFrontUp = true; - } - else if ((CValue1 == 0)) - { - PantFrontUp = false; - } - } - else - { // nie 'ezt' - odwrotne ustawienie pantografów: ^-.-^ zamiast ^-.^- - if ((CValue1 == 1)) - if ((TestFlag(Couplers[1].CouplingFlag, ctrain_controll) && (CValue2 == 1)) || - (TestFlag(Couplers[0].CouplingFlag, ctrain_controll) && (CValue2 == -1))) - { - PantFrontUp = true; - } - else - { - PantRearUp = true; - } - else if ((CValue1 == 0)) - if ((TestFlag(Couplers[1].CouplingFlag, ctrain_controll) && (CValue2 == 1)) || - (TestFlag(Couplers[0].CouplingFlag, ctrain_controll) && (CValue2 == -1))) - { - PantFrontUp = false; - } - else - { - PantRearUp = false; - } - } + auto const inputend { ( static_cast( CValue1 ) & 0x80 ) != 0 ? 1 : 0 }; + auto const inputcab { ( static_cast( CValue1 ) & 0x40 ) != 0 ? 1 : 0 }; + auto const inputoperation { static_cast( CValue1 ) & ~( 0x80 | 0x40 ) }; + auto const noswap { ( TrainType == dt_EZT ) || ( TrainType == dt_ET41 ) }; + auto swap { + ( false == noswap ) + && ( TestFlag( Couplers[ ( CValue2 == -1 ? end::rear : end::front ) ].CouplingFlag, coupling::control ) ) }; + auto const reversed { inputcab != ( CabActive != -1 ? 1 : 0 ) }; + if( reversed ) { swap = !swap; } // TODO: check whether this part has RL equivalent + OperatePantographValve( + static_cast( swap ? 1 - inputend : inputend ), + static_cast( inputoperation ), + range_t::local ); OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); } - else if (Command == "PantRear") /*Winger 160204, ABu 310105 i 030305*/ - { // Ra: uwzględnić trzeba jeszcze zgodność sprzęgów - if ((TrainType == dt_EZT) - ||(TrainType == dt_ET41)) - { //'ezt' - if ((CValue1 == 1)) - { - PantRearUp = true; - } - else if ((CValue1 == 0)) - { - PantRearUp = false; - } - } - else - { //nie 'ezt' - if ((CValue1 == 1)) - //if ostatni polaczony sprz. sterowania - if ((TestFlag(Couplers[1].CouplingFlag, ctrain_controll) && (CValue2 == 1)) || - (TestFlag(Couplers[0].CouplingFlag, ctrain_controll) && (CValue2 == -1))) - { - PantRearUp = true; - } - else - { - PantFrontUp = true; - } - else if ((CValue1 == 0)) - if ((TestFlag(Couplers[1].CouplingFlag, ctrain_controll) && (CValue2 == 1)) || - (TestFlag(Couplers[0].CouplingFlag, ctrain_controll) && (CValue2 == -1))) - { - PantRearUp = false; - } - else - { - PantFrontUp = false; - } - } + else if( Command == "PantsValve" ) { + OperatePantographsValve( static_cast( static_cast( CValue1 ) ), range_t::local ); OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); - } + } + else if( Command == "PantAllDown" ) { + DropAllPantographs( CValue1 == 1, range_t::local ); + OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); + } else if (Command == "MaxCurrentSwitch") { OK = MaxCurrentSwitch(CValue1 == 1); diff --git a/Train.cpp b/Train.cpp index 576572a5..7dc5ec43 100644 --- a/Train.cpp +++ b/Train.cpp @@ -1816,6 +1816,10 @@ void TTrain::OnCommand_reverserincrease( TTrain *Train, command_data const &Comm if( Command.action == GLFW_PRESS ) { + // HACK: master controller position isn't set in occupied vehicle in E(D)MUs + // so we do a manual check in relevant vehicle here + if( false == Train->mvControlled->EIMDirectionChangeAllow() ) { return; } + if( Train->mvOccupied->DirectionForward() ) { // aktualizacja skrajnych pojazdów w składzie if( ( Train->mvOccupied->DirActive ) @@ -1831,6 +1835,10 @@ void TTrain::OnCommand_reverserdecrease( TTrain *Train, command_data const &Comm if( Command.action == GLFW_PRESS ) { + // HACK: master controller position isn't set in occupied vehicle in E(D)MUs + // so we do a manual check in relevant vehicle here + if( false == Train->mvControlled->EIMDirectionChangeAllow() ) { return; } + if( Train->mvOccupied->DirectionBackward() ) { // aktualizacja skrajnych pojazdów w składzie if( ( Train->mvOccupied->DirActive ) @@ -1854,6 +1862,11 @@ void TTrain::OnCommand_reverserforwardhigh( TTrain *Train, command_data const &C void TTrain::OnCommand_reverserforward( TTrain *Train, command_data const &Command ) { if( Command.action == GLFW_PRESS ) { + + // HACK: master controller position isn't set in occupied vehicle in E(D)MUs + // so we do a manual check in relevant vehicle here + if( false == Train->mvControlled->EIMDirectionChangeAllow() ) { return; } + // HACK: try to move the reverser one position back, in case it's set to "high forward" OnCommand_reverserdecrease( Train, Command ); @@ -1877,6 +1890,10 @@ void TTrain::OnCommand_reverserneutral( TTrain *Train, command_data const &Comma if( Command.action == GLFW_PRESS ) { + // HACK: master controller position isn't set in occupied vehicle in E(D)MUs + // so we do a manual check in relevant vehicle here + if( false == Train->mvControlled->EIMDirectionChangeAllow() ) { return; } + while( ( Train->mvOccupied->DirActive < 0 ) && ( true == Train->mvOccupied->DirectionForward() ) ) { // all work is done in the header @@ -1892,6 +1909,10 @@ void TTrain::OnCommand_reverserbackward( TTrain *Train, command_data const &Comm if( Command.action == GLFW_PRESS ) { + // HACK: master controller position isn't set in occupied vehicle in E(D)MUs + // so we do a manual check in relevant vehicle here + if( false == Train->mvControlled->EIMDirectionChangeAllow() ) { return; } + if( Train->mvOccupied->DirActive > -1 ) { while( ( Train->mvOccupied->DirActive > -1 ) @@ -1987,14 +2008,7 @@ void TTrain::OnCommand_batterydisable( TTrain *Train, command_data const &Comman if( false == Train->mvOccupied->Battery ) { return; } // already off - if( Train->mvOccupied->BatterySwitch( false ) ) { - // side-effects - if( false == Train->mvControlled->ConverterFlag ) { - // if there's no (low voltage) power source left, drop pantographs - Train->mvControlled->PantFront( false ); - Train->mvControlled->PantRear( false ); - } - } + Train->mvOccupied->BatterySwitch( false ); } else if( Command.action == GLFW_RELEASE ) { if( Train->ggBatteryButton.type() == TGaugeType::push ) { @@ -2007,78 +2021,66 @@ void TTrain::OnCommand_batterydisable( TTrain *Train, command_data const &Comman void TTrain::OnCommand_pantographtogglefront( TTrain *Train, command_data const &Command ) { + // HACK: presence of pantograph selector prevents manual operation of the individual valves + if( Train->ggPantSelectButton.SubModel ) { return; } + if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down - if( false == Train->mvControlled->PantFrontUp ) { - // turn on... - OnCommand_pantographraisefront( Train, Command ); + auto const &pantograph { Train->mvControlled->Pantographs[ end::front ] }; + auto const state { + pantograph.valve.is_enabled + | pantograph.is_active }; // fallback for impulse switches + if( state ) { + OnCommand_pantographlowerfront( Train, Command ); } else { - // ...or turn off - OnCommand_pantographlowerfront( Train, Command ); + OnCommand_pantographraisefront( Train, Command ); } } else if( Command.action == GLFW_RELEASE ) { // impulse switches return automatically to neutral position - // NOTE: this routine is used also by dedicated raise and lower commands if( Train->mvOccupied->PantSwitchType == "impulse" ) { - if( Train->ggPantFrontButton.SubModel ) - Train->ggPantFrontButton.UpdateValue( 0.0, Train->dsbSwitch ); - // also the switch off button, in cabs which have it - if( Train->ggPantFrontButtonOff.SubModel ) - Train->ggPantFrontButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); + Train->mvControlled->OperatePantographValve( end::front, operation_t::none ); } } } void TTrain::OnCommand_pantographtogglerear( TTrain *Train, command_data const &Command ) { + // HACK: presence of pantograph selector prevents manual operation of the individual valves + if( Train->ggPantSelectButton.SubModel ) { return; } + if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down - if( false == Train->mvControlled->PantRearUp ) { - // turn on... - OnCommand_pantographraiserear( Train, Command ); + auto const &pantograph { Train->mvControlled->Pantographs[ end::rear ] }; + auto const state { + pantograph.valve.is_enabled + | pantograph.is_active }; // fallback for impulse switches + if( state ) { + OnCommand_pantographlowerrear( Train, Command ); } else { - // ...or turn off - OnCommand_pantographlowerrear( Train, Command ); + OnCommand_pantographraiserear( Train, Command ); } } else if( Command.action == GLFW_RELEASE ) { // impulse switches return automatically to neutral position - // NOTE: this routine is used also by dedicated raise and lower commands if( Train->mvOccupied->PantSwitchType == "impulse" ) { - if( Train->ggPantRearButton.SubModel ) - Train->ggPantRearButton.UpdateValue( 0.0, Train->dsbSwitch ); - // also the switch off button, in cabs which have it - if( Train->ggPantRearButtonOff.SubModel ) - Train->ggPantRearButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); + Train->mvControlled->OperatePantographValve( end::rear, operation_t::none ); } } } void TTrain::OnCommand_pantographraisefront( TTrain *Train, command_data const &Command ) { - if( Train->ggPantFrontButton.SubModel == nullptr ) { return; } + // HACK: presence of pantograph selector prevents manual operation of the individual valves + if( Train->ggPantSelectButton.SubModel ) { return; } if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down - // visual feedback - if( Train->ggPantFrontButton.SubModel ) - Train->ggPantFrontButton.UpdateValue( 1.0, Train->dsbSwitch ); - // pantograph control can have two-button setup - if( Train->ggPantFrontButtonOff.SubModel ) - Train->ggPantFrontButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); - - if( true == Train->mvControlled->PantFrontUp ) { return; } // already up - - // TBD, TODO: impulse switch should only work when the power is on? - if( Train->mvControlled->PantFront( true ) ) { - Train->mvControlled->PantFrontSP = false; - } + Train->mvControlled->OperatePantographValve( end::front, operation_t::enable ); } else if( Command.action == GLFW_RELEASE ) { - // visual feedback // NOTE: bit of a hax here, we're reusing button reset routine so we don't need a copy in every branch OnCommand_pantographtogglefront( Train, Command ); } @@ -2086,26 +2088,14 @@ void TTrain::OnCommand_pantographraisefront( TTrain *Train, command_data const & void TTrain::OnCommand_pantographraiserear( TTrain *Train, command_data const &Command ) { - if( Train->ggPantRearButton.SubModel == nullptr ) { return; } + // HACK: presence of pantograph selector prevents manual operation of the individual valves + if( Train->ggPantSelectButton.SubModel ) { return; } if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down - // visual feedback - if( Train->ggPantRearButton.SubModel ) - Train->ggPantRearButton.UpdateValue( 1.0, Train->dsbSwitch ); - // pantograph control can have two-button setup - if( Train->ggPantRearButtonOff.SubModel ) - Train->ggPantRearButtonOff.UpdateValue( 0.0, Train->dsbSwitch ); - - if( true == Train->mvControlled->PantRearUp ) { return; } // already up - - // TBD, TODO: impulse switch should only work when the power is on? - if( Train->mvControlled->PantRear( true ) ) { - Train->mvControlled->PantRearSP = false; - } + Train->mvControlled->OperatePantographValve( end::rear, operation_t::enable ); } else if( Command.action == GLFW_RELEASE ) { - // visual feedback // NOTE: bit of a hax here, we're reusing button reset routine so we don't need a copy in every branch OnCommand_pantographtogglerear( Train, Command ); } @@ -2113,38 +2103,14 @@ void TTrain::OnCommand_pantographraiserear( TTrain *Train, command_data const &C void TTrain::OnCommand_pantographlowerfront( TTrain *Train, command_data const &Command ) { - if( ( Train->ggPantFrontButton.SubModel == nullptr ) - && ( Train->ggPantFrontButtonOff.SubModel == nullptr ) ) { - // no buttons, either there's other switch types or none whatsoever - return; - } + // HACK: presence of pantograph selector prevents manual operation of the individual valves + if( Train->ggPantSelectButton.SubModel ) { return; } if( Command.action == GLFW_PRESS ) { - - if( Train->mvOccupied->PantSwitchType == "impulse" ) { - if( Train->ggPantFrontButtonOff.SubModel == nullptr ) { - // with impulse buttons we expect a dedicated switch to lower the pantograph, and if the cabin lacks it - // then another control has to be used (like pantographlowerall) - // TODO: we should have a way to define presence of cab controls without having to bind these to 3d submodels - return; - } - } - // visual feedback - if( Train->ggPantFrontButton.SubModel ) - Train->ggPantFrontButton.UpdateValue( 0.0, Train->dsbSwitch ); - // pantograph control can have two-button setup - if( Train->ggPantFrontButtonOff.SubModel ) - Train->ggPantFrontButtonOff.UpdateValue( 1.0, Train->dsbSwitch ); - - if( false == Train->mvControlled->PantFrontUp ) { return; } // already down - - // TBD, TODO: impulse switch should only work when the power is on? - if( Train->mvControlled->PantFront( false ) ) { - Train->mvControlled->PantFrontSP = false; - } + // only reacting to press, so the switch doesn't flip back and forth if key is held down + Train->mvControlled->OperatePantographValve( end::front, operation_t::disable ); } else if( Command.action == GLFW_RELEASE ) { - // visual feedback // NOTE: bit of a hax here, we're reusing button reset routine so we don't need a copy in every branch OnCommand_pantographtogglefront( Train, Command ); } @@ -2152,40 +2118,16 @@ void TTrain::OnCommand_pantographlowerfront( TTrain *Train, command_data const & void TTrain::OnCommand_pantographlowerrear( TTrain *Train, command_data const &Command ) { - if( ( Train->ggPantRearButton.SubModel == nullptr ) - && ( Train->ggPantRearButtonOff.SubModel == nullptr ) ) { - // no buttons, either there's other switch types or none whatsoever - return; - } + // HACK: presence of pantograph selector prevents manual operation of the individual valves + if( Train->ggPantSelectButton.SubModel ) { return; } if( Command.action == GLFW_PRESS ) { - - if( Train->mvOccupied->PantSwitchType == "impulse" ) { - if( Train->ggPantRearButtonOff.SubModel == nullptr ) { - // with impulse buttons we expect a dedicated switch to lower the pantograph, and if the cabin lacks it - // then another control has to be used (like pantographlowerall) - // TODO: we should have a way to define presence of cab controls without having to bind these to 3d submodels - return; - } - } - // visual feedback - if( Train->ggPantRearButton.SubModel ) - Train->ggPantRearButton.UpdateValue( 0.0, Train->dsbSwitch ); - // pantograph control can have two-button setup - if( Train->ggPantRearButtonOff.SubModel ) - Train->ggPantRearButtonOff.UpdateValue( 1.0, Train->dsbSwitch ); - - if( false == Train->mvControlled->PantRearUp ) { return; } // already down - - // TBD, TODO: impulse switch should only work when the power is on? - if( Train->mvControlled->PantRear( false ) ) { - Train->mvControlled->PantRearSP = false; - } + // only reacting to press, so the switch doesn't flip back and forth if key is held down + Train->mvControlled->OperatePantographValve( end::rear, operation_t::disable ); } else if( Command.action == GLFW_RELEASE ) { - // visual feedback // NOTE: bit of a hax here, we're reusing button reset routine so we don't need a copy in every branch - OnCommand_pantographtogglerear( Train, Command ); + OnCommand_pantographtogglefront( Train, Command ); } } @@ -2201,48 +2143,19 @@ void TTrain::OnCommand_pantographlowerall( TTrain *Train, command_data const &Co return; } - if( Train->ggPantAllDownButton.type() == TGaugeType::push ) { - // impulse switch + if( Train->ggPantAllDownButton.type() == TGaugeType::toggle ) { + // two-state switch, only cares about press events if( Command.action == GLFW_PRESS ) { - // press the button - // since we're just lowering all potential pantographs we don't need to test for state and effect - // front... - Train->mvControlled->PantFrontSP = false; - Train->mvControlled->PantFront( false ); - // ...and rear - Train->mvControlled->PantRearSP = false; - Train->mvControlled->PantRear( false ); + Train->mvControlled->DropAllPantographs( false == Train->mvControlled->PantAllDown ); // visual feedback - Train->ggPantAllDownButton.UpdateValue( 1.0, Train->dsbSwitch ); - } - else if( Command.action == GLFW_RELEASE ) { - // release the button - // visual feedback - Train->ggPantAllDownButton.UpdateValue( 0.0 ); + Train->ggPantAllDownButton.UpdateValue( ( Train->mvControlled->PantAllDown ? 1.0 : 0.0 ), Train->dsbSwitch ); } } else { - // two-state switch, only cares about press events - if( Command.action == GLFW_PRESS ) { - // HACK: use current switch position to determine (intended) state - if( Train->ggPantAllDownButton.GetDesiredValue() < 0.05 ) { - // currenty inactive, activate it - // since we're just lowering all potential pantographs we don't need to test for state and effect - // front... - Train->mvControlled->PantFrontSP = false; - Train->mvControlled->PantFront( false ); - // ...and rear - Train->mvControlled->PantRearSP = false; - Train->mvControlled->PantRear( false ); - // visual feedback - Train->ggPantAllDownButton.UpdateValue( 1.0, Train->dsbSwitch ); - } - else { - // currently active, move it back to neutral position - // visual feedback - Train->ggPantAllDownButton.UpdateValue( 0.0 ); - } - } + // impulse switch + Train->mvControlled->DropAllPantographs( Command.action == GLFW_PRESS ); + // visual feedback + Train->ggPantAllDownButton.UpdateValue( ( Command.action == GLFW_PRESS ? 1.0 : 0.0 ), Train->dsbSwitch ); } } @@ -2250,12 +2163,16 @@ void TTrain::OnCommand_pantographselectnext( TTrain *Train, command_data const & if( Command.action != GLFW_PRESS ) { return; } + if( Train->ggPantSelectButton.SubModel == nullptr ) { return; } + Train->change_pantograph_selection( 1 ); } void TTrain::OnCommand_pantographselectprevious( TTrain *Train, command_data const &Command ) { - if( Command.action != GLFW_PRESS ) { return; } + if( Command.action != GLFW_PRESS ) { return; } + + if( Train->ggPantSelectButton.SubModel == nullptr ) { return; } Train->change_pantograph_selection( -1 ); } @@ -2264,59 +2181,47 @@ void TTrain::OnCommand_pantographraiseselected( TTrain *Train, command_data cons if( Command.action == GLFW_REPEAT ) { return; } - if( Train->ggPantSelectedButton.type() == TGaugeType::push ) { - // impulse switch - if( Command.action == GLFW_PRESS ) { - // visual feedback - Train->ggPantSelectedButton.UpdateValue( 1.0, Train->dsbSwitch ); - // raise selected - Train->change_pantograph_selection_state( true ); - } - else if( Command.action == GLFW_RELEASE ) { + if( Command.action == GLFW_PRESS ) { + // raise selected + Train->mvControlled->OperatePantographsValve( + Train->ggPantSelectedButton.type() == TGaugeType::toggle ? + operation_t::enable : + operation_t::enable_on ); + // visual feedback + Train->ggPantSelectedButton.UpdateValue( 1.0, Train->dsbSwitch ); + } + else if( Command.action == GLFW_RELEASE ) { + if( Train->ggPantSelectedButton.type() != TGaugeType::toggle ) { + Train->mvControlled->OperatePantographsValve( operation_t::enable_off ); // visual feedback Train->ggPantSelectedButton.UpdateValue( 0.0, Train->dsbSwitch ); } } - else { - // two-state switch, only cares about press events - if( Command.action == GLFW_PRESS ) { - // visual feedback - Train->ggPantSelectedButton.UpdateValue( 1.0, Train->dsbSwitch ); - // raise selected - Train->change_pantograph_selection_state( true ); - } - } } void TTrain::OnCommand_pantographlowerselected( TTrain *Train, command_data const &Command ) { if( Command.action == GLFW_REPEAT ) { return; } - if( Train->ggPantSelectedDownButton.type() == TGaugeType::push ) { - // impulse switch - if( Command.action == GLFW_PRESS ) { - // visual feedback - Train->ggPantSelectedDownButton.UpdateValue( 1.0, Train->dsbSwitch ); - // lower selected - Train->change_pantograph_selection_state( false ); - } - else if( Command.action == GLFW_RELEASE ) { - // visual feedback - Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); - } + if( Command.action == GLFW_PRESS ) { + // lower selected + Train->mvControlled->OperatePantographsValve( + Train->ggPantSelectedDownButton.type() == TGaugeType::toggle ? + operation_t::disable : + operation_t::disable_on ); + // visual feedback + Train->ggPantSelectedDownButton.UpdateValue( 1.0, Train->dsbSwitch ); } - else { - // two-state switch, only cares about press events - if( Command.action == GLFW_PRESS ) { + else if( Command.action == GLFW_RELEASE ) { + if( Train->ggPantSelectedDownButton.type() != TGaugeType::toggle ) { + Train->mvControlled->OperatePantographsValve( operation_t::disable_off ); // visual feedback Train->ggPantSelectedDownButton.UpdateValue( 0.0, Train->dsbSwitch ); - // lower selected - Train->change_pantograph_selection_state( false ); } } } -void TTrain::change_pantograph_selection( int const Change, bool const Force ) { +void TTrain::change_pantograph_selection( int const Change ) { auto const initialstate { m_pantselection }; @@ -2324,55 +2229,16 @@ void TTrain::change_pantograph_selection( int const Change, bool const Force ) { // visual feedback ggPantSelectButton.UpdateValue( m_pantselection ); - if( ( m_pantselection == initialstate ) && ( false == Force ) ) { return; } // no change, nothing to do + if( m_pantselection == initialstate ) { return; } // no change, nothing to do - // crude way to determine whether pantograph state change is automatic - // if there's button to raise/lower selected panthograph, we presume the change has to be invoked manually - if( ggPantSelectedButton.SubModel != nullptr ) { return; } - - // raise or lower pantographs according to new requested state - auto const swapends{ cab_to_end() != end::front }; + // configure pantograph valves matching the new state + auto const swapends { cab_to_end() != end::front }; // check desired states for both pantographs; value: whether the pantograph should be raised auto const frontstate{ ( m_pantselection == 2 ) || ( m_pantselection == ( swapends ? 1 : 3 ) ) }; auto const rearstate{ ( m_pantselection == 2 ) || ( m_pantselection == ( swapends ? 3 : 1 ) ) }; - // potentially adjust front pantograph state - if( mvControlled->PantFrontUp != frontstate ) { - // TBD, TODO: impulse switch should only work when the power is on? - if( mvControlled->PantFront( frontstate ) == frontstate ) { - mvControlled->PantFrontSP = false; - } - } - // potentially adjust rear pantograph state - if( mvControlled->PantRearUp != rearstate ) { - // TBD, TODO: impulse switch should only work when the power is on? - if( mvControlled->PantRear( rearstate ) == rearstate ) { - mvControlled->PantRearSP = false; - } - } -} - -void TTrain::change_pantograph_selection_state( bool const State ) { - - auto const swapends { cab_to_end() != end::front }; - // check desired states for both pantographs; value: whether the pantograph should be raised - auto const frontselected { ( m_pantselection == 0 ) || ( m_pantselection == 2 ) || ( m_pantselection == ( swapends ? 1 : 3 ) ) }; - auto const rearselected { ( m_pantselection == 0 ) || ( m_pantselection == 2 ) || ( m_pantselection == ( swapends ? 3 : 1 ) ) }; - // potentially adjust front pantograph state - if( ( true == frontselected ) - && ( mvControlled->PantFrontUp != State ) ) { - // TBD, TODO: impulse switch should only work when the power is on? - if( mvControlled->PantFront( State ) == State ) { - mvControlled->PantFrontSP = false; - } - } - // potentially adjust rear pantograph state - if( ( true == rearselected ) - && ( mvControlled->PantRearUp != State ) ) { - // TBD, TODO: impulse switch should only work when the power is on? - if( mvControlled->PantRear( State ) == State ) { - mvControlled->PantRearSP = false; - } - } + // potentially adjust pantograph valves + mvControlled->OperatePantographValve( end::front, ( frontstate ? operation_t::enable : operation_t::disable ) ); + mvControlled->OperatePantographValve( end::rear, ( rearstate ? operation_t::enable : operation_t::disable ) ); } void TTrain::OnCommand_pantographcompressorvalvetoggle( TTrain *Train, command_data const &Command ) { @@ -3031,11 +2897,6 @@ void TTrain::OnCommand_converterdisable( TTrain *Train, command_data const &Comm && ( false == TestFlag( Train->mvControlled->EngDmgFlag, 4 ) ) ) { Train->mvControlled->ConvOvldFlag = false; } - // if there's no (low voltage) power source left, drop pantographs - if( false == Train->mvControlled->Battery ) { - Train->mvControlled->PantFront( false ); - Train->mvControlled->PantRear( false ); - } } } else if( Command.action == GLFW_RELEASE ) { @@ -5909,8 +5770,8 @@ bool TTrain::Update( double const Deltatime ) cCode[i] = p->MoverParameters->TypeName[p->MoverParameters->TypeName.length() - 1]; asCarName[i] = p->name(); if( p->MoverParameters->EnginePowerSource.SourceType == TPowerSource::CurrentCollector ) { - bPants[iUnitNo - 1][end::front] = ( bPants[iUnitNo - 1][end::front] || p->MoverParameters->PantFrontUp ); - bPants[iUnitNo - 1][end::rear] = ( bPants[iUnitNo - 1][end::rear] || p->MoverParameters->PantRearUp ); + bPants[iUnitNo - 1][end::front] = ( bPants[iUnitNo - 1][end::front] || p->MoverParameters->Pantographs[end::front].is_active ); + bPants[iUnitNo - 1][end::rear] = ( bPants[iUnitNo - 1][end::rear] || p->MoverParameters->Pantographs[end::rear].is_active ); } bComp[iUnitNo - 1][0] = (bComp[iUnitNo - 1][0] || p->MoverParameters->CompressorAllow || (p->MoverParameters->CompressorStart == start_t::automatic)); bSlip[i] = p->MoverParameters->SlippingWheels; @@ -6784,14 +6645,15 @@ bool TTrain::Update( double const Deltatime ) ggRadioVolumePrevious.Update(); ggRadioVolumeNext.Update(); ggDepartureSignalButton.Update(); - +/* ggPantFrontButton.Update(); ggPantRearButton.Update(); - ggPantSelectedButton.Update(); ggPantFrontButtonOff.Update(); ggPantRearButtonOff.Update(); - ggPantSelectedDownButton.Update(); +*/ ggPantAllDownButton.Update(); + ggPantSelectedDownButton.Update(); + ggPantSelectedButton.Update(); ggPantSelectButton.Update(); ggPantCompressorButton.Update(); ggPantCompressorValve.Update(); @@ -6898,7 +6760,7 @@ bool TTrain::Update( double const Deltatime ) mvControlled->AntiSlippingBrake(); } } - +/* // NOTE: crude way to have the pantographs go back up if they're dropped due to insufficient pressure etc // TODO: rework it into something more elegant, when redoing the whole consist/unit/cab etc arrangement if( ( DynamicObject->Mechanik == nullptr ) @@ -6931,6 +6793,7 @@ bool TTrain::Update( double const Deltatime ) } } } +*/ /* // check whether we should raise the pantographs, based on volume in pantograph tank // NOTE: disabled while switch state isn't preserved while moving between compartments @@ -8100,13 +7963,15 @@ void TTrain::clear_cab_controls() ggConverterOffButton.Clear(); ggConverterLocalButton.Clear(); ggMainButton.Clear(); +/* ggPantFrontButton.Clear(); ggPantRearButton.Clear(); - ggPantSelectedButton.Clear(); ggPantFrontButtonOff.Clear(); ggPantRearButtonOff.Clear(); - ggPantSelectedDownButton.Clear(); +*/ ggPantAllDownButton.Clear(); + ggPantSelectedButton.Clear(); + ggPantSelectedDownButton.Clear(); ggPantSelectButton.Clear(); ggPantCompressorButton.Clear(); ggPantCompressorValve.Clear(); @@ -8250,34 +8115,36 @@ void TTrain::set_cab_controls( int const Cab ) { } ggRadioChannelSelector.PutValue( RadioChannel() - 1 ); // pantographs +/* if( mvOccupied->PantSwitchType != "impulse" ) { if( ggPantFrontButton.SubModel ) { ggPantFrontButton.PutValue( - ( mvControlled->PantFrontUp ? + ( mvControlled->Pantographs[end::front].valve.is_enabled ? 1.f : 0.f ) ); } if( ggPantFrontButtonOff.SubModel ) { ggPantFrontButtonOff.PutValue( - ( mvControlled->PantFrontUp ? - 0.f : - 1.f ) ); + ( mvControlled->Pantographs[end::front].valve.is_disabled ? + 1.f : + 0.f ) ); } } if( mvOccupied->PantSwitchType != "impulse" ) { if( ggPantRearButton.SubModel ) { ggPantRearButton.PutValue( - ( mvControlled->PantRearUp ? + ( mvControlled->Pantographs[end::rear].valve.is_enabled ? 1.f : 0.f ) ); } if( ggPantRearButtonOff.SubModel ) { ggPantRearButtonOff.PutValue( - ( mvControlled->PantRearUp ? - 0.f : - 1.f ) ); + ( mvControlled->Pantographs[end::rear].valve.is_disabled ? + 1.f : + 0.f ) ); } } +*/ // front/end pantograph selection is relative to occupied cab m_pantselection = ( m_pantselection == 1 ? ( cab_to_end( Cab ) == cab_to_end() ? 1 : 3 ) : @@ -8287,34 +8154,16 @@ void TTrain::set_cab_controls( int const Cab ) { ggPantSelectButton.PutValue( m_pantselection ); } if( ggPantSelectedButton.type() == TGaugeType::toggle ) { - if( ggPantSelectButton.SubModel ) { - auto const swapends{ cab_to_end( Cab ) != end::front }; - auto const pantraised{ ( - m_pantselection == 0 ? false : - m_pantselection == 1 ? ( swapends ? mvControlled->PantFrontUp : mvControlled->PantRearUp ) : - m_pantselection == 2 ? ( mvControlled->PantFrontUp & mvControlled->PantRearUp ) : - m_pantselection == 3 ? ( swapends ? mvControlled->PantRearUp : mvControlled->PantFrontUp ) : - false ) }; - ggPantSelectedButton.PutValue( - ( pantraised ? - 1.f : - 0.f ) ); - } + ggPantSelectedButton.PutValue( + ( mvControlled->PantsValve.is_enabled ? + 1.f : + 0.f ) ); } if( ggPantSelectedDownButton.type() == TGaugeType::toggle ) { - if( ggPantSelectedDownButton.SubModel ) { - auto const swapends{ cab_to_end( Cab ) != end::front }; - auto const pantraised{ ( - m_pantselection == 0 ? false : - m_pantselection == 1 ? ( swapends ? mvControlled->PantFrontUp : mvControlled->PantRearUp ) : - m_pantselection == 2 ? ( mvControlled->PantFrontUp & mvControlled->PantRearUp ) : - m_pantselection == 3 ? ( swapends ? mvControlled->PantRearUp : mvControlled->PantFrontUp ) : - false ) }; - ggPantSelectedDownButton.PutValue( - ( pantraised ? - 0.f : - 1.f ) ); - } + ggPantSelectedDownButton.PutValue( + ( mvControlled->PantsValve.is_disabled ? + 1.f : + 0.f ) ); } // auxiliary compressor ggPantCompressorValve.PutValue( @@ -8805,10 +8654,12 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con { "radiovolume_sw:", ggRadioVolumeSelector }, { "radiovolumeprev_sw:", ggRadioVolumePrevious }, { "radiovolumenext_sw:", ggRadioVolumeNext }, +/* { "pantfront_sw:", ggPantFrontButton }, { "pantrear_sw:", ggPantRearButton }, { "pantfrontoff_sw:", ggPantFrontButtonOff }, { "pantrearoff_sw:", ggPantRearButtonOff }, +*/ { "pantalloff_sw:", ggPantAllDownButton }, { "pantselected_sw:", ggPantSelectedButton }, { "pantselectedoff_sw:", ggPantSelectedDownButton }, @@ -8879,7 +8730,11 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con std::unordered_map const autoboolgauges = { { "doormode_sw:", &mvOccupied->Doors.remote_only }, { "doorstep_sw:", &mvOccupied->Doors.step_enabled }, - { "coolingfans_sw:", &mvControlled->RVentForceOn } + { "coolingfans_sw:", &mvControlled->RVentForceOn }, + { "pantfront_sw:", &mvControlled->Pantographs[end::front].valve.is_enabled }, + { "pantrear_sw:", &mvControlled->Pantographs[end::rear].valve.is_enabled }, + { "pantfrontoff_sw:", &mvControlled->Pantographs[end::front].valve.is_disabled }, + { "pantrearoff_sw:", &mvControlled->Pantographs[end::rear].valve.is_disabled } }; { auto lookup = autoboolgauges.find( Label ); diff --git a/Train.h b/Train.h index e57b61b0..03ae09e9 100644 --- a/Train.h +++ b/Train.h @@ -156,8 +156,7 @@ class TTrain { // sets the motor connector button in paired unit to specified state void set_paired_open_motor_connectors_button( bool const State ); // helper, common part of pantograph selection methods - void change_pantograph_selection( int const Change, bool const Force = false ); - void change_pantograph_selection_state( bool const State ); + void change_pantograph_selection( int const Change ); // update function subroutines void update_sounds( double const Deltatime ); void update_sounds_runningnoise( sound_source &Sound ); @@ -538,10 +537,12 @@ public: // reszta może by?publiczna TGauge ggDepartureSignalButton; // Winger 160204 - obsluga pantografow - ZROBIC +/* TGauge ggPantFrontButton; TGauge ggPantRearButton; TGauge ggPantFrontButtonOff; // EZT TGauge ggPantRearButtonOff; +*/ TGauge ggPantAllDownButton; TGauge ggPantSelectedButton; TGauge ggPantSelectedDownButton; diff --git a/audiorenderer.cpp b/audiorenderer.cpp index 31289f08..ff4a22d9 100644 --- a/audiorenderer.cpp +++ b/audiorenderer.cpp @@ -155,14 +155,18 @@ openal_source::sync_with( sound_properties const &State ) { ::alSourcefv( id, AL_POSITION, glm::value_ptr( glm::vec3() ) ); } // gain - if( ( State.soundproofing_stamp != properties.soundproofing_stamp ) - || ( State.gain != properties.gain ) ) { + if( ( State.gain != properties.gain ) + || ( State.soundproofing_stamp != properties.soundproofing_stamp ) ) { // gain value has changed properties.gain = State.gain; properties.soundproofing = State.soundproofing; properties.soundproofing_stamp = State.soundproofing_stamp; - ::alSourcef( id, AL_GAIN, properties.gain * properties.soundproofing ); + auto const range { ( + sound_range >= 0 ? + sound_range : + 5 ) }; // range of -1 means sound of unlimited range, positioned at the listener + ::alSourcef( id, AL_REFERENCE_DISTANCE, range * ( 1.f / 16.f ) * properties.soundproofing ); } if( sound_range > 0 ) { auto const rangesquared { sound_range * sound_range }; diff --git a/audiorenderer.h b/audiorenderer.h index 3d2de107..84543c15 100644 --- a/audiorenderer.h +++ b/audiorenderer.h @@ -13,6 +13,8 @@ http://mozilla.org/MPL/2.0/. #include "ResourceManager.h" #include "uitranscripts.h" +#define EU07_SOUND_PROOFINGUSESRANGE + class opengl_renderer; class sound_source; diff --git a/driveruipanels.cpp b/driveruipanels.cpp index 7ca3461c..b7da9755 100644 --- a/driveruipanels.cpp +++ b/driveruipanels.cpp @@ -596,8 +596,9 @@ debug_panel::update_section_vehicle( std::vector &Output ) { ( mover.Battery ? 'B' : '.' ), ( mover.Mains ? 'M' : '.' ), ( mover.FuseFlag ? '!' : '.' ), - ( mover.PantRearUp ? ( mover.PantRearVolt > 0.0 ? 'O' : 'o' ) : '.' ), - ( mover.PantFrontUp ? ( mover.PantFrontVolt > 0.0 ? 'P' : 'p' ) : '.' ), + ( mover.PantsValve.is_active ? '+' : '.' ), + ( mover.Pantographs[ end::rear ].valve.is_enabled ? ( mover.Pantographs[ end::rear ].valve.is_active ? 'O' : 'o' ) : '.' ), + ( mover.Pantographs[end::front].valve.is_enabled ? ( mover.Pantographs[ end::front ].valve.is_active ? 'P' : 'p' ) : '.' ), ( mover.PantPressLockActive ? '!' : ( mover.PantPressSwitchActive ? '*' : '.' ) ), ( mover.WaterPump.is_active ? 'W' : ( false == mover.WaterPump.breaker ? '-' : ( mover.WaterPump.is_enabled ? 'w' : '.' ) ) ), ( true == mover.WaterHeater.is_damaged ? '!' : ( mover.WaterHeater.is_active ? 'H' : ( false == mover.WaterHeater.breaker ? '-' : ( mover.WaterHeater.is_enabled ? 'h' : '.' ) ) ) ), diff --git a/opengl33renderer.cpp b/opengl33renderer.cpp index fc18c9dc..24f4edf8 100644 --- a/opengl33renderer.cpp +++ b/opengl33renderer.cpp @@ -2508,10 +2508,12 @@ bool opengl33_renderer::Render_cab(TDynamicObject const *Dynamic, float const Li setup_sunlight_intensity(Dynamic->fShade); } - // crude way to light the cabin, until we have something more complete in place - glm::vec3 old_ambient = light_ubs.ambient; - light_ubs.ambient += Dynamic->InteriorLight * Lightlevel; - light_ubo->update(light_ubs); + auto const old_ambient { light_ubs.ambient }; + if( Lightlevel > 0.f ) { + // crude way to light the cabin, until we have something more complete in place + light_ubs.ambient += ( Dynamic->InteriorLight * Lightlevel ) * static_cast( clamp( 1.25 - Global.fLuminance, 0.0, 1.0 ) ); + light_ubo->update( light_ubs ); + } // render if (true == Alpha) @@ -2532,8 +2534,10 @@ bool opengl33_renderer::Render_cab(TDynamicObject const *Dynamic, float const Li } // restore ambient - light_ubs.ambient = old_ambient; - light_ubo->update(light_ubs); + if( Lightlevel > 0.f ) { + light_ubs.ambient = old_ambient; + light_ubo->update( light_ubs ); + } break; } diff --git a/openglrenderer.cpp b/openglrenderer.cpp index 124f4404..16baee8f 100644 --- a/openglrenderer.cpp +++ b/openglrenderer.cpp @@ -2403,7 +2403,11 @@ opengl_renderer::Render_cab( TDynamicObject const *Dynamic, float const Lightlev } if( Lightlevel > 0.f ) { // crude way to light the cabin, until we have something more complete in place - ::glLightModelfv( GL_LIGHT_MODEL_AMBIENT, glm::value_ptr( Dynamic->InteriorLight * Lightlevel ) ); + ::glLightModelfv( + GL_LIGHT_MODEL_AMBIENT, + glm::value_ptr( + glm::vec3( m_baseambient ) + + ( Dynamic->InteriorLight * Lightlevel ) * static_cast( clamp( 1.25 - Global.fLuminance, 0.0, 1.0 ) ) ) ); } // render if( true == Alpha ) { diff --git a/sound.cpp b/sound.cpp index f3bd17ba..fa9c94fc 100644 --- a/sound.cpp +++ b/sound.cpp @@ -917,8 +917,8 @@ sound_source::update_location() { m_properties.location = location(); } -float const EU07_SOUNDPROOFING_STRONG { 0.25f }; -float const EU07_SOUNDPROOFING_SOME { 0.65f }; +float const EU07_SOUNDPROOFING_STRONG { 0.5f }; // 0.25 after squaring +float const EU07_SOUNDPROOFING_SOME { 0.8f }; // ~0.65 after squaring float const EU07_SOUNDPROOFING_NONE { 1.f }; bool diff --git a/translation.cpp b/translation.cpp index 4b52c8bd..004e20f3 100644 --- a/translation.cpp +++ b/translation.cpp @@ -70,7 +70,7 @@ init() { "Name: %s%s\nLoad: %.0f %s\nStatus: %s%s\nCouplers:\n front: %s\n rear: %s", ", owned by: ", "none", - "Devices: %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%s%s\nPower transfers: %.0f@%.0f%s%s[%.0f]%s%s%.0f@%.0f", + "Devices: %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%s%s\nPower transfers: %.0f@%.0f%s%s[%.0f]%s%s%.0f@%.0f", " radio: ", " oil pressure: ", "Controllers:\n master: %d(%d), secondary: %s\nEngine output: %.1f, current: %.0f\nRevolutions:\n engine: %.0f, motors: %.0f\n engine fans: %.0f, motor fans: %.0f+%.0f, cooling fans: %.0f+%.0f", @@ -245,7 +245,7 @@ init() { "Nazwa: %s%s\nLadunek: %.0f %s\nStatus: %s%s\nSprzegi:\n przedni: %s\n tylny: %s", ", wlasciciel: ", "wolny", - "Urzadzenia: %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%s%s\nTransfer pradow: %.0f@%.0f%s%s[%.0f]%s%s%.0f@%.0f", + "Urzadzenia: %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%s%s\nTransfer pradow: %.0f@%.0f%s%s[%.0f]%s%s%.0f@%.0f", " radio: ", " cisn.oleju: ", "Nastawniki:\n glowny: %d(%d), dodatkowy: %s\nMoc silnika: %.1f, prad silnika: %.0f\nObroty:\n silnik: %.0f, motory: %.0f\n went.silnika: %.0f, went.motorow: %.0f+%.0f, went.chlodnicy: %.0f+%.0f", diff --git a/version.h b/version.h index be631c54..510595e8 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 20 -#define VERSION_MINOR 116 +#define VERSION_MINOR 120 #define VERSION_REVISION 0