From aac16c55be3961ccb1b3b43d9c2722737e8fa147 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Mon, 16 Apr 2018 20:08:30 +0200 Subject: [PATCH 1/6] basic diesel engine temperature calculations heating/cooling subsystem devices --- Driver.cpp | 75 ++++-- Driver.h | 1 + DynObj.cpp | 33 ++- McZapkie/MOVER.h | 89 ++++++- McZapkie/Mover.cpp | 595 +++++++++++++++++++++++++++++++++++++++------ Train.cpp | 302 ++++++++++++++++++++++- Train.h | 24 +- command.cpp | 15 ++ command.h | 15 ++ keyboardinput.cpp | 30 +++ mouseinput.cpp | 12 + translation.h | 5 + uilayer.cpp | 14 +- 13 files changed, 1105 insertions(+), 105 deletions(-) diff --git a/Driver.cpp b/Driver.cpp index 6967e5f1..eb44fdf0 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -2226,10 +2226,12 @@ bool TController::PrepareEngine() if (AIControllFlag) { // część wykonawcza dla sterowania przez komputer mvOccupied->BatterySwitch( true ); - if( ( mvOccupied->EngineType == DieselElectric ) - || ( mvOccupied->EngineType == DieselEngine ) ) { - mvOccupied->FuelPumpSwitch( true ); - mvOccupied->OilPumpSwitch( true ); + if( ( mvControlling->EngineType == DieselElectric ) + || ( mvControlling->EngineType == DieselEngine ) ) { + mvControlling->OilPumpSwitch( true ); + if( true == UpdateHeating() ) { + mvControlling->FuelPumpSwitch( true ); + } } if (mvControlling->EnginePowerSource.SourceType == CurrentCollector) { // jeśli silnikowy jest pantografującym @@ -2298,22 +2300,13 @@ bool TController::PrepareEngine() else if (false == mvControlling->Mains) { while (DecSpeed(true)) ; // zerowanie napędu -/* - if( ( mvOccupied->EngineType == DieselEngine ) - || ( mvOccupied->EngineType == DieselElectric ) ) { - // start helper devices before spinning up the engine - // TODO: replace with dedicated diesel engine subsystems - mvOccupied->ConverterSwitch( true ); - mvOccupied->CompressorSwitch( true ); - } -*/ if( mvOccupied->TrainType == dt_SN61 ) { // specjalnie dla SN61 żeby nie zgasł if( mvControlling->RList[ mvControlling->MainCtrlPos ].Mn == 0 ) { mvControlling->IncMainCtrl( 1 ); } } - OK = mvControlling->MainSwitch(true); + mvControlling->MainSwitch(true); /* if (mvControlling->EngineType == DieselEngine) { // Ra 2014-06: dla SN61 trzeba wrzucić pierwszą pozycję - nie wiem, czy tutaj... @@ -2345,6 +2338,7 @@ bool TController::PrepareEngine() } else OK = false; + OK = OK && (mvOccupied->ActiveDir != 0) && (mvControlling->CompressorAllow); if (OK) { @@ -5168,6 +5162,59 @@ TController::UpdateSituation(double dt) { } // switch (OrderList[OrderPos]) } +// configures vehicle heating given current situation; returns: true if vehicle can be operated normally, false otherwise +bool +TController::UpdateHeating() { + + switch( mvControlling->EngineType ) { + + case DieselElectric: + case DieselEngine: { + + auto const &heat { mvControlling->dizel_heat }; + + // determine whether there's need to enable the water heater + // if the heater has configured maximum temperature, it'll disable itself automatically, so we can leave it always running + // otherwise enable the heater only to maintain minimum required temperature + auto const lowtemperature { ( + ( ( heat.water.config.temp_min > 0 ) && ( heat.temperatura1 < heat.water.config.temp_min + ( mvControlling->WaterHeater.is_active ? 5 : 0 ) ) ) + || ( ( heat.water_aux.config.temp_min > 0 ) && ( heat.temperatura2 < heat.water_aux.config.temp_min + ( mvControlling->WaterHeater.is_active ? 5 : 0 ) ) ) + || ( ( heat.oil.config.temp_min > 0 ) && ( heat.To < heat.oil.config.temp_min + ( mvControlling->WaterHeater.is_active ? 5 : 0 ) ) ) ) }; + auto const heateron { ( + ( mvControlling->WaterHeater.config.temp_max > 0 ) + || ( true == lowtemperature ) ) }; + if( true == heateron ) { + // make sure the water pump is running before enabling the heater + if( false == mvControlling->WaterPump.is_active ) { + mvControlling->WaterPumpBreakerSwitch( true ); + mvControlling->WaterPumpSwitch( true ); + } + if( true == mvControlling->WaterPump.is_active ) { + mvControlling->WaterHeaterBreakerSwitch( true ); + mvControlling->WaterHeaterSwitch( true ); + mvControlling->WaterCircuitsLinkSwitch( true ); + } + } + else { + // no need to heat anything up, switch the heater off + mvControlling->WaterCircuitsLinkSwitch( false ); + mvControlling->WaterHeaterSwitch( false ); + mvControlling->WaterHeaterBreakerSwitch( false ); + // optionally turn off the water pump as well + if( mvControlling->WaterPump.start_type != start::battery ) { + mvControlling->WaterPumpSwitch( false ); + mvControlling->WaterPumpBreakerSwitch( false ); + } + } + + return ( false == lowtemperature ); + } + default: { + return true; + } + } +} + void TController::JumpToNextOrder() { // wykonanie kolejnej komendy z tablicy rozkazów if (OrderList[OrderPos] != Wait_for_orders) diff --git a/Driver.h b/Driver.h index aef33df6..214df307 100644 --- a/Driver.h +++ b/Driver.h @@ -318,6 +318,7 @@ private: void PutCommand(std::string NewCommand, double NewValue1, double NewValue2, const TLocation &NewLocation, TStopReason reason = stopComm); bool PutCommand( std::string NewCommand, double NewValue1, double NewValue2, glm::dvec3 const *NewLocation, TStopReason reason = stopComm ); void UpdateSituation(double dt); // uruchamiac przynajmniej raz na sekundę + bool UpdateHeating(); // procedury dotyczace rozkazow dla maszynisty // uaktualnia informacje o prędkości void SetVelocity(double NewVel, double NewVelNext, TStopReason r = stopNone); diff --git a/DynObj.cpp b/DynObj.cpp index f1d3ea43..81b465a5 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -1978,7 +1978,34 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" if( Random( 0, 100 ) <= flatchance ) { MoverParameters->WheelFlat += fixedflatsize + Random( 0, randomflatsize ); } - } + } // wheel + else if( ( ActPar.size() >= 2 ) + && ( ActPar[ 0 ] == 'T' ) ) { + // temperature + ActPar.erase( 0, 1 ); + + auto setambient { false }; + + while( false == ActPar.empty() ) { + switch( ActPar[ 0 ] ) { + case 'A': { + // cold start, set all temperatures to ambient level + setambient = true; + ActPar.erase( 0, 1 ); + break; + } + default: { + // unrecognized key + ActPar.erase( 0, 1 ); + break; + } + } + } + if( true == setambient ) { + // TODO: pull ambient temperature from environment data + MoverParameters->dizel_HeatSet( 15.f ); + } + } // temperature /* else if (ActPar.substr(0, 1) == "") // tu mozna wpisac inny prefiks i inne rzeczy { // jakies inne prefiksy @@ -2707,7 +2734,7 @@ histerezę czasową, aby te tryby pracy nie przełączały się zbyt szybko. bool TDynamicObject::Update(double dt, double dt1) { - if (dt == 0) + if (dt1 == 0) return true; // Ra: pauza if (!MoverParameters->PhysicActivation && !MechInside) // to drugie, bo będąc w maszynowym blokuje się fizyka @@ -2867,7 +2894,7 @@ bool TDynamicObject::Update(double dt, double dt1) tmpTraction.TractionVoltage = v; } else { - NoVoltTime += dt; + NoVoltTime += dt1; if( NoVoltTime > 0.2 ) { // jeśli brak zasilania dłużej niż 0.2 sekundy (25km/h pod izolatorem daje 0.15s) // Ra 2F1H: prowizorka, trzeba przechować napięcie, żeby nie wywalało WS pod izolatorem diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 6a2d8bf1..97231ad9 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -165,7 +165,8 @@ enum range { enum start { manual, automatic, - manualwithautofallback + manualwithautofallback, + battery }; // recognized vehicle light locations and types; can be combined enum light { @@ -640,6 +641,78 @@ struct oil_pump { float pressure_present { 0.f }; }; +struct water_pump { + + bool breaker { true }; // device is allowed to operate + bool is_enabled { false }; // device is requested to operate + bool is_active { false }; // device is working + start start_type { start::manual }; +}; + +struct water_heater { + + bool breaker { true }; // device is allowed to operate + bool is_enabled { false }; // device is requested to operate + bool is_active { false }; // device is working + bool is_damaged { false }; // device is damaged + + struct heater_config_t { + float temp_min { -1 }; // lowest accepted temperature + float temp_max { -1 }; // highest accepted temperature + } config; +}; + +struct heat_data { + // input, state of relevant devices + bool cooling { false }; // TODO: user controlled device, implement +// bool okienko { true }; // window in the engine compartment + // system configuration + bool auxiliary_water_circuit { false }; // cooling system has an extra water circuit + // heat exchange factors + double kw { 0.35 }; + double kv { 0.6 }; + double kfe { 1.0 }; + double kfs { 80.0 }; + double kfo { 25.0 }; + double kfo2 { 25.0 }; + // system parts + struct fluid_circuit_t { + + struct circuit_config_t { + float temp_min { -1 }; // lowest accepted temperature + float temp_max { -1 }; // highest accepted temperature + float temp_cooling { -1 }; // active cooling activation point + float temp_flow { -1 }; // fluid flow activation point + bool shutters { false }; // the radiator has shutters to assist the cooling + } config; + bool is_cold { false }; // fluid is too cold + bool is_warm { false }; // fluid is too hot + bool is_hot { false }; // fluid temperature crossed cooling threshold + bool is_flowing { false }; // fluid is being pushed through the circuit + } water, + water_aux, + oil; + // output, state of affected devices + bool PA { false }; // malfunction flag + float rpmw { 0.0 }; // current main circuit fan revolutions + float rpmwz { 0.0 }; // desired main circuit fan revolutions + bool zaluzje1 { false }; + float rpmw2 { 0.0 }; // current auxiliary circuit fan revolutions + float rpmwz2 { 0.0 }; // desired auxiliary circuit fan revolutions + bool zaluzje2 { false }; + // output, temperatures + float Te { 15.0 }; // ambient temperature TODO: get it from environment data + // NOTE: by default the engine is initialized in warm, startup-ready state + float Ts { 50.0 }; // engine temperature + float To { 45.0 }; // oil temperature + float Tsr { 50.0 }; // main circuit radiator temperature (?) + float Twy { 50.0 }; // main circuit water temperature + float Tsr2 { 40.0 }; // secondary circuit radiator temperature (?) + float Twy2 { 40.0 }; // secondary circuit water temperature + float temperatura1 { 50.0 }; + float temperatura2 { 40.0 }; +}; + class TMoverParameters { // Ra: wrapper na kod pascalowy, przejmujący jego funkcje Q: 20160824 - juz nie wrapper a klasa bazowa :) public: @@ -928,6 +1001,10 @@ public: bool ConverterFlag = false; /*! czy wlaczona przetwornica NBMX*/ fuel_pump FuelPump; oil_pump OilPump; + water_pump WaterPump; + water_heater WaterHeater; + bool WaterCircuitsLink { false }; // optional connection between water circuits + heat_data dizel_heat; int BrakeCtrlPos = -2; /*nastawa hamulca zespolonego*/ double BrakeCtrlPosR = 0.0; /*nastawa hamulca zespolonego - plynna dla FV4a*/ @@ -1123,6 +1200,7 @@ public: void UpdateBatteryVoltage(double dt); double ComputeMovement(double dt, double dt1, const TTrackShape &Shape, TTrackParam &Track, TTractionParam &ElectricTraction, const TLocation &NewLoc, TRotation &NewRot); //oblicza przesuniecie pojazdu double FastComputeMovement(double dt, const TTrackShape &Shape, TTrackParam &Track, const TLocation &NewLoc, TRotation &NewRot); //oblicza przesuniecie pojazdu - wersja zoptymalizowana + void compute_movement_( double const Deltatime ); double ShowEngineRotation(int VehN); // Q ******************************************************************************************* @@ -1215,6 +1293,11 @@ public: /*--funkcje dla lokomotyw*/ bool DirectionBackward(void);/*! kierunek ruchu*/ + bool WaterPumpBreakerSwitch( bool State, int const Notify = range::consist ); // water pump breaker state toggle + bool WaterPumpSwitch( bool State, int const Notify = range::consist ); // water pump state toggle + bool WaterHeaterBreakerSwitch( bool State, int const Notify = range::consist ); // water heater breaker state toggle + bool WaterHeaterSwitch( bool State, int const Notify = range::consist ); // water heater state toggle + bool WaterCircuitsLinkSwitch( bool State, int const Notify = range::consist ); // water circuits link state toggle bool FuelPumpSwitch( bool State, int const Notify = range::consist ); // fuel pump state toggle bool OilPumpSwitch( bool State, int const Notify = range::consist ); // oil pump state toggle bool MainSwitch( bool const State, int const Notify = range::consist );/*! wylacznik glowny*/ @@ -1223,6 +1306,8 @@ public: /*-funkcje typowe dla lokomotywy elektrycznej*/ void ConverterCheck( double const Timestep ); // przetwornica + void WaterPumpCheck( double const Timestep ); + void WaterHeaterCheck( double const Timestep ); void FuelPumpCheck( double const Timestep ); void OilPumpCheck( double const Timestep ); bool FuseOn(void); //bezpiecznik nadamiary @@ -1256,6 +1341,8 @@ public: bool dizel_AutoGearCheck(void); double dizel_fillcheck(int mcp); double dizel_Momentum(double dizel_fill, double n, double dt); + void dizel_HeatSet( float const Value ); + void dizel_Heat( double const dt ); bool dizel_StartupCheck(); bool dizel_Update(double dt); diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 3076ec0f..b340ca79 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -1372,46 +1372,7 @@ double TMoverParameters::ComputeMovement(double dt, double dt1, const TTrackShap dL = 0; // koniec procedury, tu nastepuja dodatkowe procedury pomocnicze - - // sprawdzanie i ewentualnie wykonywanie->kasowanie poleceń - if (LoadStatus > 0) // czas doliczamy tylko jeśli trwa (roz)ładowanie - LastLoadChangeTime += dt; // czas (roz)ładunku - - RunInternalCommand(); - - // automatyczny rozruch - if (EngineType == ElectricSeriesMotor) - - if (AutoRelayCheck()) - SetFlag(SoundFlag, sound::relay); - - if( ( EngineType == DieselEngine ) - || ( EngineType == DieselElectric ) ) { - if( dizel_Update( dt ) ) { - SetFlag( SoundFlag, sound::relay ); - } - } - // uklady hamulcowe: - if (VeselVolume > 0) - Compressor = CompressedVolume / VeselVolume; - else - { - Compressor = 0; - CompressorFlag = false; - }; - ConverterCheck(dt); - if (CompressorSpeed > 0.0) // sprężarka musi mieć jakąś niezerową wydajność - CompressorCheck(dt); //żeby rozważać jej załączenie i pracę - UpdateBrakePressure(dt); - UpdatePipePressure(dt); - UpdateBatteryVoltage(dt); - UpdateScndPipePressure(dt); // druga rurka, youBy - // hamulec antypoślizgowy - wyłączanie - if ((BrakeSlippingTimer > 0.8) && (ASBType != 128)) // ASBSpeed=0.8 - Hamulec->ASB(0); - BrakeSlippingTimer += dt; - // automatic doors - update_autonomous_doors( dt ); + compute_movement_( dt ); // security system if (!DebugModeFlag) SecuritySystemCheck(dt1); @@ -1491,16 +1452,32 @@ double TMoverParameters::FastComputeMovement(double dt, const TTrackShape &Shape dL = 0; // koniec procedury, tu nastepuja dodatkowe procedury pomocnicze + compute_movement_( dt ); + + return d; +}; + +// updates shared between 'fast' and regular movement computation methods +void TMoverParameters::compute_movement_( double const Deltatime ) { // sprawdzanie i ewentualnie wykonywanie->kasowanie poleceń if (LoadStatus > 0) // czas doliczamy tylko jeśli trwa (roz)ładowanie - LastLoadChangeTime += dt; // czas (roz)ładunku + LastLoadChangeTime += Deltatime; // czas (roz)ładunku RunInternalCommand(); - if (EngineType == DieselEngine) - if (dizel_Update(dt)) + // automatyczny rozruch + if (EngineType == ElectricSeriesMotor) + + if (AutoRelayCheck()) SetFlag(SoundFlag, sound::relay); + + if( ( EngineType == DieselEngine ) + || ( EngineType == DieselElectric ) ) { + if( dizel_Update( Deltatime ) ) { + SetFlag( SoundFlag, sound::relay ); + } + } // uklady hamulcowe: if (VeselVolume > 0) Compressor = CompressedVolume / VeselVolume; @@ -1509,22 +1486,24 @@ double TMoverParameters::FastComputeMovement(double dt, const TTrackShape &Shape Compressor = 0; CompressorFlag = false; }; - ConverterCheck(dt); - if (CompressorSpeed > 0.0) // sprężarka musi mieć jakąś niezerową wydajność - CompressorCheck(dt); //żeby rozważać jej załączenie i pracę - UpdateBrakePressure(dt); - UpdatePipePressure(dt); - UpdateScndPipePressure(dt); // druga rurka, youBy - UpdateBatteryVoltage(dt); - // hamulec antyposlizgowy - wyłączanie - if ((BrakeSlippingTimer > 0.8) && (ASBType != 128)) // ASBSpeed=0.8 - Hamulec->ASB(0); - BrakeSlippingTimer += dt; + ConverterCheck(Deltatime); + if( CompressorSpeed > 0.0 ) { + // sprężarka musi mieć jakąś niezerową wydajność żeby rozważać jej załączenie i pracę + CompressorCheck( Deltatime ); + } + UpdateBrakePressure(Deltatime); + UpdatePipePressure(Deltatime); + UpdateBatteryVoltage(Deltatime); + UpdateScndPipePressure(Deltatime); // druga rurka, youBy + + if( ( BrakeSlippingTimer > 0.8 ) && ( ASBType != 128 ) ) { // ASBSpeed=0.8 + // hamulec antypoślizgowy - wyłączanie + Hamulec->ASB( 0 ); + } + BrakeSlippingTimer += Deltatime; // automatic doors - update_autonomous_doors( dt ); - - return d; -}; + update_autonomous_doors( Deltatime ); +} double TMoverParameters::ShowEngineRotation(int VehN) { // Zwraca wartość prędkości obrotowej silnika wybranego pojazdu. Do 3 pojazdów (3×SN61). @@ -1576,6 +1555,35 @@ void TMoverParameters::ConverterCheck( double const Timestep ) { } }; +// water pump status check +void TMoverParameters::WaterPumpCheck( double const Timestep ) { + // NOTE: breaker override with start type is sm42 specific hack, replace with ability to define the presence of the breaker + WaterPump.is_active = ( + ( true == Battery ) + && ( true == WaterPump.breaker ) + && ( ( true == WaterPump.is_enabled ) || ( WaterPump.start_type == start::battery ) ) ); +} + +// water heater status check +void TMoverParameters::WaterHeaterCheck( double const Timestep ) { + + WaterHeater.is_active = ( + ( false == WaterHeater.is_damaged ) + && ( true == Battery ) + && ( true == WaterHeater.is_enabled ) + && ( true == WaterHeater.breaker ) + && ( ( WaterHeater.config.temp_min < 0 ) || ( dizel_heat.temperatura1 < WaterHeater.config.temp_min ) ) ); + + if( ( WaterHeater.config.temp_max > 0 ) + && ( dizel_heat.temperatura1 > WaterHeater.config.temp_max ) ) { + WaterHeater.is_active = false; + } + + WaterHeater.is_damaged |= ( + ( true == WaterHeater.is_active ) + && ( false == WaterPump.is_active ) ); +} + // fuel pump status update void TMoverParameters::FuelPumpCheck( double const Timestep ) { @@ -1590,12 +1598,12 @@ void TMoverParameters::FuelPumpCheck( double const Timestep ) { // oil pump status update void TMoverParameters::OilPumpCheck( double const Timestep ) { - OilPump.is_active = - ( ( true == Battery ) - && ( OilPump.start_type == start::manual ? ( OilPump.is_enabled ) : - OilPump.start_type == start::automatic ? ( dizel_startup || Mains ) : - OilPump.start_type == start::manualwithautofallback ? ( OilPump.is_enabled || dizel_startup || Mains ) : - false ) ); // shouldn't ever get this far but, eh + OilPump.is_active = ( + ( true == Battery ) + && ( OilPump.start_type == start::manual ? ( OilPump.is_enabled ) : + OilPump.start_type == start::automatic ? ( dizel_startup || Mains ) : + OilPump.start_type == start::manualwithautofallback ? ( OilPump.is_enabled || dizel_startup || Mains ) : + false ) ); // shouldn't ever get this far but, eh auto const maxrevolutions { EngineType == DieselEngine ? @@ -1604,13 +1612,13 @@ void TMoverParameters::OilPumpCheck( double const Timestep ) { auto const minpressure { OilPump.pressure_minimum > 0.f ? OilPump.pressure_minimum : - 0.1f }; // arbitrary fallback value + 0.15f }; // arbitrary fallback value auto const maxpressure { 0.65f }; // arbitrary value OilPump.pressure_target = ( - false == OilPump.is_active ? 0.f : - enrot > 0.1 ? std::max( minpressure, maxpressure * clamp( enrot / maxrevolutions, 0.0, 1.0 ) ) * OilPump.resource_amount : - minpressure ); + enrot > 0.1 ? interpolate( minpressure, maxpressure, static_cast( clamp( enrot / maxrevolutions, 0.0, 1.0 ) ) ) * OilPump.resource_amount : + true == OilPump.is_active ? minpressure : + 0.f ); if( OilPump.pressure_present < OilPump.pressure_target ) { // TODO: scale change rate from 0.01-0.05 with oil/engine temperature/idle time @@ -2396,6 +2404,131 @@ bool TMoverParameters::AntiSlippingButton(void) return (AntiSlippingBrake() /*|| Sandbox(true)*/); } +// water pump breaker state toggle +bool TMoverParameters::WaterPumpBreakerSwitch( bool State, int const Notify ) { +/* + if( FuelPump.start_type == start::automatic ) { + // automatic fuel pump ignores 'manual' state commands + return false; + } +*/ + bool const initialstate { WaterPump.breaker }; + + WaterPump.breaker = State; + + if( Notify != range::local ) { + SendCtrlToNext( + "WaterPumpBreakerSwitch", + ( WaterPump.breaker ? 1 : 0 ), + CabNo, + ( Notify == range::unit ? + coupling::control | coupling::permanent : + coupling::control ) ); + } + + return ( WaterPump.breaker != initialstate ); +} + +// water pump state toggle +bool TMoverParameters::WaterPumpSwitch( bool State, int const Notify ) { + + if( WaterPump.start_type == start::battery ) { + // automatic fuel pump ignores 'manual' state commands + return false; + } + + bool const initialstate { WaterPump.is_enabled }; + + WaterPump.is_enabled = State; + + if( Notify != range::local ) { + SendCtrlToNext( + "WaterPumpSwitch", + ( WaterPump.is_enabled ? 1 : 0 ), + CabNo, + ( Notify == range::unit ? + coupling::control | coupling::permanent : + coupling::control ) ); + } + + return ( WaterPump.is_enabled != initialstate ); +} + +// water heater breaker state toggle +bool TMoverParameters::WaterHeaterBreakerSwitch( bool State, int const Notify ) { +/* + if( FuelPump.start_type == start::automatic ) { + // automatic fuel pump ignores 'manual' state commands + return false; + } +*/ + bool const initialstate { WaterHeater.breaker }; + + WaterHeater.breaker = State; + + if( Notify != range::local ) { + SendCtrlToNext( + "WaterHeaterBreakerSwitch", + ( WaterHeater.breaker ? 1 : 0 ), + CabNo, + ( Notify == range::unit ? + coupling::control | coupling::permanent : + coupling::control ) ); + } + + return ( WaterHeater.breaker != initialstate ); +} + +// water heater state toggle +bool TMoverParameters::WaterHeaterSwitch( bool State, int const Notify ) { +/* + if( FuelPump.start_type == start::automatic ) { + // automatic fuel pump ignores 'manual' state commands + return false; + } +*/ + bool const initialstate { WaterHeater.is_enabled }; + + WaterHeater.is_enabled = State; + + if( Notify != range::local ) { + SendCtrlToNext( + "WaterHeaterSwitch", + ( WaterHeater.is_enabled ? 1 : 0 ), + CabNo, + ( Notify == range::unit ? + coupling::control | coupling::permanent : + coupling::control ) ); + } + + return ( WaterHeater.is_enabled != initialstate ); +} + +// water circuits link state toggle +bool TMoverParameters::WaterCircuitsLinkSwitch( bool State, int const Notify ) { + + if( false == dizel_heat.auxiliary_water_circuit ) { + // can't link the circuits if the vehicle only has one + return false; + } + + bool const initialstate { WaterCircuitsLink }; + + WaterCircuitsLink = State; + + if( Notify != range::local ) { + SendCtrlToNext( + "WaterCircuitsLinkSwitch", + ( WaterCircuitsLink ? 1 : 0 ), + CabNo, + ( Notify == range::unit ? + coupling::control | coupling::permanent : + coupling::control ) ); + } + + return ( WaterCircuitsLink != initialstate ); +} + // fuel pump state toggle bool TMoverParameters::FuelPumpSwitch( bool State, int const Notify ) { @@ -4417,12 +4550,30 @@ double TMoverParameters::TractionForce(double dt) } case DieselElectric: { + // NOTE: for this type RventRot is the speed of motor blowers; we also update radiator fans while at it if( true == Mains ) { // TBD, TODO: currently ignores RVentType, fix this? - RventRot += clamp( DElist[ MainCtrlPos ].RPM / 60.0 - RventRot, -100.0, 50.0 ) * dt; + RventRot += clamp( enrot - RventRot, -100.0, 50.0 ) * dt; + dizel_heat.rpmw += clamp( dizel_heat.rpmwz - dizel_heat.rpmw, -100.f, 50.f ) * dt; + dizel_heat.rpmw += clamp( dizel_heat.rpmwz - dizel_heat.rpmw, -100.f, 50.f ) * dt; } else { RventRot *= std::max( 0.0, 1.0 - RVentSpeed * dt ); + dizel_heat.rpmw *= std::max( 0.0, 1.0 - dizel_heat.rpmw * dt ); + dizel_heat.rpmw2 *= std::max( 0.0, 1.0 - dizel_heat.rpmw2 * dt ); + } + break; + } + + case DieselEngine: { + // NOTE: we update only radiator fans, as vehicles with diesel engine don't have other ventilators + if( true == Mains ) { + dizel_heat.rpmw += clamp( dizel_heat.rpmwz - dizel_heat.rpmw, -100.f, 50.f ) * dt; + dizel_heat.rpmw += clamp( dizel_heat.rpmwz - dizel_heat.rpmw, -100.f, 50.f ) * dt; + } + else { + dizel_heat.rpmw *= std::max( 0.0, 1.0 - dizel_heat.rpmw * dt ); + dizel_heat.rpmw2 *= std::max( 0.0, 1.0 - dizel_heat.rpmw2 * dt ); } break; } @@ -4521,11 +4672,9 @@ double TMoverParameters::TractionForce(double dt) if( MainSwitch( false, ( TrainType == dt_EZT ? range::unit : range::local ) ) ) // TODO: check whether we need to send this EMU-wide EventFlag = true; // wywalanie szybkiego z powodu niewłaściwego napięcia - if (((DynamicBrakeType == dbrake_automatic) || (DynamicBrakeType == dbrake_switch)) && - (DynamicBrakeFlag)) + if (((DynamicBrakeType == dbrake_automatic) || (DynamicBrakeType == dbrake_switch)) && (DynamicBrakeFlag)) Itot = Im * 2; // 2x2 silniki w EP09 - else if ((TrainType == dt_EZT) && (Imin == IminLo) && - (ScndS)) // yBARC - boczniki na szeregu poprawnie + else if ((TrainType == dt_EZT) && (Imin == IminLo) && (ScndS)) // yBARC - boczniki na szeregu poprawnie Itot = Im; else Itot = Im * RList[MainCtrlActualPos].Bn; // prad silnika * ilosc galezi @@ -5847,6 +5996,12 @@ bool TMoverParameters::dizel_StartupCheck() { dizel_startup = false; } } + // test the water circuits and water temperature + if( true == dizel_heat.PA ) { + engineisready = false; + // TBD, TODO: reset startup procedure depending on pump and heater control mode + dizel_startup = false; + } return engineisready; } @@ -5857,6 +6012,8 @@ bool TMoverParameters::dizel_StartupCheck() { // ************************************************************************************************* bool TMoverParameters::dizel_Update(double dt) { + WaterPumpCheck( dt ); + WaterHeaterCheck( dt ); OilPumpCheck( dt ); FuelPumpCheck( dt ); if( ( true == dizel_startup ) @@ -5897,6 +6054,8 @@ bool TMoverParameters::dizel_Update(double dt) { dizel_fill = dizel_fill + fillspeed * dt * ( dizel_fillcheck( MainCtrlPos ) - dizel_fill ); } + dizel_Heat( dt ); + return DU; } @@ -6103,6 +6262,205 @@ double TMoverParameters::dizel_Momentum(double dizel_fill, double n, double dt) return gearMoment; } +// sets component temperatures to specified value +void TMoverParameters::dizel_HeatSet( float const Value ) { + + dizel_heat.Te = // TODO: don't include ambient temperature, pull it from environment data instead + dizel_heat.Ts = + dizel_heat.To = + dizel_heat.Tsr = + dizel_heat.Twy = + dizel_heat.Tsr2 = + dizel_heat.Twy2 = + dizel_heat.temperatura1 = + dizel_heat.temperatura2 = Value; +} + +// calculates diesel engine temperature and heat transfers +// adapted from scripts written by adamst +// NOTE: originally executed twice per second +void TMoverParameters::dizel_Heat( double const dt ) { + + auto const qs { 44700.0 }; + auto const Cs { 11000.0 }; + auto const Cw { 4.189 }; + auto const Co { 1.885 }; + auto const gwmin { 400.0 }; + auto const gwmax { 4000.0 }; + auto const gwmin2 { 400.0 }; + auto const gwmax2 { 4000.0 }; + + auto const engineon { ( Mains ? 1 : 0 ) }; + auto const engineoff { ( Mains ? 0 : 1 ) }; + auto const rpm { enrot * 60 }; + // TODO: calculate this once and cache for further use, instead of doing it repeatedly all over the place + auto const maxrevolutions { ( + EngineType == DieselEngine ? dizel_nmax * 60 : + EngineType == DieselElectric ? DElist[ MainCtrlPosNo ].RPM : + std::numeric_limits::max() ) }; // shouldn't ever get here but, eh + auto const revolutionsfactor { clamp( rpm / maxrevolutions, 0.0, 1.0 ) }; + auto const waterpump { WaterPump.is_active ? 1 : 0 }; + + auto const gw = engineon * interpolate( gwmin, gwmax, revolutionsfactor ) + waterpump * 1000 + engineoff * 200; + auto const gw2 = engineon * interpolate( gwmin2, gwmax2, revolutionsfactor ) + waterpump * 1000 + engineoff * 200; + auto const gwO = interpolate( gwmin, gwmax, revolutionsfactor ); + + dizel_heat.water.is_cold = ( + ( dizel_heat.water.config.temp_min > 0 ) + && ( dizel_heat.temperatura1 < dizel_heat.water.config.temp_min - ( Mains ? 5 : 0 ) ) ); + dizel_heat.water.is_hot = ( + ( dizel_heat.water.config.temp_max > 0 ) + && ( dizel_heat.temperatura1 > dizel_heat.water.config.temp_max - ( dizel_heat.water.is_hot ? 8 : 0 ) ) ); + dizel_heat.water_aux.is_cold = ( + ( dizel_heat.water_aux.config.temp_min > 0 ) + && ( dizel_heat.temperatura2 < dizel_heat.water_aux.config.temp_min - ( Mains ? 5 : 0 ) ) ); + dizel_heat.water_aux.is_hot = ( + ( dizel_heat.water_aux.config.temp_max > 0 ) + && ( dizel_heat.temperatura2 > dizel_heat.water_aux.config.temp_max - ( dizel_heat.water_aux.is_hot ? 8 : 0 ) ) ); + dizel_heat.oil.is_cold = ( + ( dizel_heat.oil.config.temp_min > 0 ) + && ( dizel_heat.To < dizel_heat.oil.config.temp_min - ( Mains ? 5 : 0 ) ) ); + dizel_heat.oil.is_hot = ( + ( dizel_heat.oil.config.temp_max > 0 ) + && ( dizel_heat.To > dizel_heat.oil.config.temp_max - ( dizel_heat.oil.is_hot ? 8 : 0 ) ) ); + + auto const PT = ( + ( false == dizel_heat.water.is_cold ) + && ( false == dizel_heat.water.is_hot ) + && ( false == dizel_heat.water_aux.is_cold ) + && ( false == dizel_heat.water_aux.is_hot ) + && ( false == dizel_heat.oil.is_cold ) + && ( false == dizel_heat.oil.is_hot ) /* && ( false == awaria_termostatow ) */ ) /* || PTp */; + auto const PPT = ( false == PT ) /* && ( false == PPTp ) */; + dizel_heat.PA = ( /* ( ( !zamkniecie or niedomkniecie ) and !WBD ) || */ PPT /* || nurnik || ( woda < 7 ) */ ) /* && ( !PAp ) */; + + // engine heat transfers + auto const Ge { engineon * ( 0.21 * EnginePower + 12 ) / 3600 }; + // TODO: replace fixed heating power cost with more accurate calculation + auto const obciazenie { engineon * ( ( EnginePower / 950 ) + ( Heating ? HeatingPower : 0 ) + 70 ) }; + auto const Qd { qs * Ge - obciazenie }; + // silnik oddaje czesc ciepla do wody chlodzacej, a takze pewna niewielka czesc do otoczenia, modyfikowane przez okienko + auto const Qs { ( Qd - ( dizel_heat.kfs * ( dizel_heat.Ts - dizel_heat.Tsr ) ) - ( dizel_heat.kfe * /* ( 0.3 + 0.7 * ( dizel_heat.okienko ? 1 : 0 ) ) * */ ( dizel_heat.Ts - dizel_heat.Te ) ) ) }; + auto const dTss { Qs / Cs }; + dizel_heat.Ts += ( dTss * dt ); + + // oil heat transfers + // olej oddaje cieplo do wody gdy krazy przez wymiennik ciepla == wlaczona pompka lub silnik + auto const dTo { ( + dizel_heat.auxiliary_water_circuit ? + ( ( dizel_heat.kfo * ( dizel_heat.Ts - dizel_heat.To ) ) - ( dizel_heat.kfs * ( 0.3 ) * ( dizel_heat.To - dizel_heat.Tsr2 ) ) ) / ( gwO * Co ) : + ( ( dizel_heat.kfo * ( dizel_heat.Ts - dizel_heat.To ) ) - ( dizel_heat.kfo2 * ( dizel_heat.To - dizel_heat.Tsr ) ) ) / ( gwO * Co ) ) }; + dizel_heat.To += ( dTo * dt ); + + // heater +/* + if( typ == "SP45" ) + Qp = (float)( podgrzewacz and ( true == WaterPump.is_active ) and ( Twy < 55 ) and ( Twy2 < 55 ) ) * 1000; + else +*/ + auto const Qp = ( ( ( true == WaterHeater.is_active ) && ( true == WaterPump.is_active ) && ( dizel_heat.Twy < 60 ) && ( dizel_heat.Twy2 < 60 ) ) ? 1 : 0 ) * 1000; + + auto const kurek07 { 1 }; // unknown/unimplemented device TBD, TODO: identify and implement? + + if( true == dizel_heat.auxiliary_water_circuit ) { + // auxiliary water circuit setup + dizel_heat.water_aux.is_warm = ( + ( true == dizel_heat.cooling ) + || ( ( true == Mains ) + && ( BatteryVoltage > 70 ) /* && !bezpompy && !awaria_chlodzenia && !WS10 */ + && ( dizel_heat.water_aux.config.temp_cooling > 0 ) + && ( dizel_heat.temperatura2 > dizel_heat.water_aux.config.temp_cooling - ( dizel_heat.water_aux.is_warm ? 8 : 0 ) ) ) ); + auto const PTC2 { ( dizel_heat.water_aux.is_warm /*or PTC2p*/ ? 1 : 0 ) }; + dizel_heat.rpmwz2 = PTC2 * 80 * rpm / ( ( 0.5 * rpm ) + 500 ); + dizel_heat.zaluzje2 = ( dizel_heat.water_aux.config.shutters ? PTC2 : true ); // no shutters is an equivalent to having them open + auto const zaluzje2 { ( dizel_heat.zaluzje2 ? 1 : 0 ) }; + // auxiliary water circuit heat transfer values + auto const kf2 { kurek07 * ( ( dizel_heat.kw * ( 0.3 + 0.7 * zaluzje2 ) ) * dizel_heat.rpmw2 + ( dizel_heat.kv * ( 0.3 + 0.7 * zaluzje2 ) * Vel / 3.6 ) ) + 2 }; + auto const dTs2 { ( ( dizel_heat.kfs * ( 0.3 ) * ( dizel_heat.To - dizel_heat.Tsr2 ) ) ) / ( gw2 * Cw ) }; + // przy otwartym kurku B ma³y obieg jest dogrzewany przez du¿y - stosujemy przy korzystaniu z podgrzewacza oraz w zimie + auto const Qch2 { -kf2 * ( dizel_heat.Tsr2 - dizel_heat.Te ) + ( 80 * ( true == WaterCircuitsLink ? 1 : 0 ) * ( dizel_heat.Twy - dizel_heat.Tsr2 ) ) }; + auto const dTch2 { Qch2 / ( gw2 * Cw ) }; + // auxiliary water circuit heat transfers finalization + // NOTE: since primary circuit doesn't read data from the auxiliary one, we can pretty safely finalize auxiliary updates before touching the primary circuit + auto const Twe2 { dizel_heat.Twy2 + ( dTch2 * dt ) }; + dizel_heat.Twy2 = Twe2 + ( dTs2 * dt ); + dizel_heat.Tsr2 = 0.5 * ( dizel_heat.Twy2 + Twe2 ); + dizel_heat.temperatura2 = dizel_heat.Twy2; + } + // primary water circuit setup + dizel_heat.water.is_flowing = ( + ( dizel_heat.water.config.temp_flow < 0 ) + || ( dizel_heat.temperatura1 > dizel_heat.water.config.temp_flow - ( dizel_heat.water.is_flowing ? 5 : 0 ) ) ); + auto const obieg { ( dizel_heat.water.is_flowing ? 1 : 0 ) }; + dizel_heat.water.is_warm = ( + ( true == dizel_heat.cooling ) + || ( ( true == Mains ) + && ( BatteryVoltage > 70 ) /* && !bezpompy && !awaria_chlodzenia && !WS10 */ + && ( dizel_heat.water.config.temp_cooling > 0 ) + && ( dizel_heat.temperatura1 > dizel_heat.water.config.temp_cooling - ( dizel_heat.water.is_warm ? 8 : 0 ) ) ) ); + auto const PTC1 { ( dizel_heat.water.is_warm /*or PTC1p*/ ? 1 : 0 ) }; + dizel_heat.rpmwz = PTC1 * 80 * rpm / ( ( 0.5 * rpm ) + 500 ); + dizel_heat.zaluzje1 = ( dizel_heat.water.config.shutters ? PTC1 : true ); // no shutters is an equivalent to having them open + auto const zaluzje1 { ( dizel_heat.zaluzje1 ? 1 : 0 ) }; + // primary water circuit heat transfer values + auto const kf { obieg * kurek07 * ( ( dizel_heat.kw * ( 0.3 + 0.7 * zaluzje1 ) ) * dizel_heat.rpmw + ( dizel_heat.kv * ( 0.3 + 0.7 * zaluzje1 ) * Vel / 3.6 ) + 3 ) + 2 }; + auto const dTs { ( + dizel_heat.auxiliary_water_circuit ? + ( ( dizel_heat.kfs * ( dizel_heat.Ts - dizel_heat.Tsr ) ) ) / ( gw * Cw ) : + ( ( dizel_heat.kfs * ( dizel_heat.Ts - dizel_heat.Tsr ) ) + ( dizel_heat.kfo2 * ( dizel_heat.To - dizel_heat.Tsr ) ) ) / ( gw * Cw ) ) }; + auto const Qch { -kf * ( dizel_heat.Tsr - dizel_heat.Te ) + Qp }; + auto const dTch { Qch / ( gw * Cw ) }; + // primary water circuit heat transfers finalization + auto const Twe { dizel_heat.Twy + ( dTch * dt ) }; + dizel_heat.Twy = Twe + ( dTs * dt ); + dizel_heat.Tsr = 0.5 * ( dizel_heat.Twy + Twe ); + dizel_heat.temperatura1 = dizel_heat.Twy; +/* + fuelConsumed = fuelConsumed + ( Ge * 0.5 ); + + while( fuelConsumed >= 0.83 ) { + fuelConsumed = fuelConsumed - 0.83; + fuelQueue.DestroyProductMatching( null, 1 ); + }//if + + if( engineon ) + temp_turbo = temp_turbo + 0.3 * ( t_pozycja ); + if( t_pozycja == 0 and cisnienie > 0.04 ) + temp_turbo = temp_turbo - 1; + + if( temp_turbo > 400 ) + temp_turbo = 400; + if( temp_turbo < 0 ) + temp_turbo = 0; + + if( temp_turbo > 50 and cisnienie < 0.05 ) + timer_turbo = timer_turbo + 1; + + if( temp_turbo == 0 ) + timer_turbo = 0; + + if( timer_turbo > 360 ) { + awaria_turbo = true; + timer_turbo = 400; + } + + if( Ts < 50 ) + p_odpal = 3; + if( Ts > 49 and Ts < 76 ) + p_odpal = 4; + if( Ts > 75 ) + p_odpal = 7; + + stukanie = stukanie or awaria_oleju; + + if( awaria_oleju == true and ilosc_oleju > 0 ) { + ilosc_oleju = ilosc_oleju - ( 0.002 * rpm / 1500 ); + } + if( awaria_oleju == true and cisnienie < 0.06 ) + damage = 1; +*/ +} + // ************************************************************************************************* // Q: 20160713 // Test zakończenia załadunku / rozładunku @@ -7775,6 +8133,19 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) { lookup->second : start::manual; } + + // water pump + { + std::map starts { + { "Manual", start::manual }, + { "Battery", start::battery } + }; + auto lookup = starts.find( extract_value( "WaterStart", line ) ); + WaterPump.start_type = + lookup != starts.end() ? + lookup->second : + start::manual; + } } void TMoverParameters::LoadFIZ_Light( std::string const &line ) { @@ -7987,7 +8358,31 @@ void TMoverParameters::LoadFIZ_Engine( std::string const &Input ) { default: { // nothing here } - } + } // engine type + + // engine cooling factore + extract_value( dizel_heat.kw, "HeatKW", Input, "" ); + extract_value( dizel_heat.kv, "HeatKV", Input, "" ); + extract_value( dizel_heat.kfe, "HeatKFE", Input, "" ); + extract_value( dizel_heat.kfs, "HeatKFS", Input, "" ); + extract_value( dizel_heat.kfo, "HeatKFO", Input, "" ); + extract_value( dizel_heat.kfo2, "HeatKFO2", Input, "" ); + // engine cooling systems + extract_value( dizel_heat.water.config.temp_min, "WaterMinTemperature", Input, "" ); + extract_value( dizel_heat.water.config.temp_max, "WaterMaxTemperature", Input, "" ); + extract_value( dizel_heat.water.config.temp_flow, "WaterFlowTemperature", Input, "" ); + extract_value( dizel_heat.water.config.temp_cooling, "WaterCoolingTemperature", Input, "" ); + extract_value( dizel_heat.water.config.shutters, "WaterShutters", Input, "" ); + extract_value( dizel_heat.auxiliary_water_circuit, "WaterAuxCircuit", Input, "" ); + extract_value( dizel_heat.water_aux.config.temp_min, "WaterAuxMinTemperature", Input, "" ); + extract_value( dizel_heat.water_aux.config.temp_max, "WaterAuxMaxTemperature", Input, "" ); + extract_value( dizel_heat.water_aux.config.temp_cooling, "WaterAuxCoolingTemperature", Input, "" ); + extract_value( dizel_heat.water_aux.config.shutters, "WaterAuxShutters", Input, "" ); + extract_value( dizel_heat.oil.config.temp_min, "OilMinTemperature", Input, "" ); + extract_value( dizel_heat.oil.config.temp_max, "OilMaxTemperature", Input, "" ); + // water heater + extract_value( WaterHeater.config.temp_min, "HeaterMinTemperature", Input, "" ); + extract_value( WaterHeater.config.temp_max, "HeaterMaxTemperature", Input, "" ); } void TMoverParameters::LoadFIZ_Switches( std::string const &Input ) { @@ -8671,15 +9066,63 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C OK = BrakeReleaser(Round(CValue1)); // samo się przesyła dalej // OK:=SendCtrlToNext(command,CValue1,CValue2); //to robiło kaskadę 2^n } + else if( Command == "WaterPumpBreakerSwitch" ) { +/* + if( FuelPump.start_type != start::automatic ) { + // automatic fuel pump ignores 'manual' state commands +*/ + WaterPump.breaker = ( CValue1 == 1 ); +/* + } +*/ + OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); + } + else if( Command == "WaterPumpSwitch" ) { + + if( WaterPump.start_type != start::battery ) { + // automatic fuel pump ignores 'manual' state commands + WaterPump.is_enabled = ( CValue1 == 1 ); + } + OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); + } + else if( Command == "WaterHeaterBreakerSwitch" ) { +/* + if( FuelPump.start_type != start::automatic ) { + // automatic fuel pump ignores 'manual' state commands +*/ + WaterHeater.breaker = ( CValue1 == 1 ); +/* + } +*/ + OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); + } + else if( Command == "WaterHeaterSwitch" ) { +/* + if( FuelPump.start_type != start::automatic ) { + // automatic fuel pump ignores 'manual' state commands +*/ + WaterHeater.is_enabled = ( CValue1 == 1 ); +/* + } +*/ + OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); + } + else if( Command == "WaterCircuitsLinkSwitch" ) { + if( true == dizel_heat.auxiliary_water_circuit ) { + // can only link circuits if the vehicle has more than one of them + WaterCircuitsLink = ( CValue1 == 1 ); + } + OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); + } else if (Command == "FuelPumpSwitch") { - if( FuelPump.start_type == start::manual ) { + if( FuelPump.start_type != start::automatic ) { // automatic fuel pump ignores 'manual' state commands FuelPump.is_enabled = ( CValue1 == 1 ); } OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); } else if (Command == "OilPumpSwitch") { - if( OilPump.start_type == start::manual ) { + if( OilPump.start_type != start::automatic ) { // automatic pump ignores 'manual' state commands OilPump.is_enabled = ( CValue1 == 1 ); } diff --git a/Train.cpp b/Train.cpp index 60301c0d..9a991709 100644 --- a/Train.cpp +++ b/Train.cpp @@ -228,6 +228,21 @@ TTrain::commandhandler_map const TTrain::m_commandhandlers = { { user_command::oilpumptoggle, &TTrain::OnCommand_oilpumptoggle }, { user_command::oilpumpenable, &TTrain::OnCommand_oilpumpenable }, { user_command::oilpumpdisable, &TTrain::OnCommand_oilpumpdisable }, + { user_command::waterheaterbreakertoggle, &TTrain::OnCommand_waterheaterbreakertoggle }, + { user_command::waterheaterbreakerclose, &TTrain::OnCommand_waterheaterbreakerclose }, + { user_command::waterheaterbreakeropen, &TTrain::OnCommand_waterheaterbreakeropen }, + { user_command::waterheatertoggle, &TTrain::OnCommand_waterheatertoggle }, + { user_command::waterheaterenable, &TTrain::OnCommand_waterheaterenable }, + { user_command::waterheaterdisable, &TTrain::OnCommand_waterheaterdisable }, + { user_command::waterpumpbreakertoggle, &TTrain::OnCommand_waterpumpbreakertoggle }, + { user_command::waterpumpbreakerclose, &TTrain::OnCommand_waterpumpbreakerclose }, + { user_command::waterpumpbreakeropen, &TTrain::OnCommand_waterpumpbreakeropen }, + { user_command::waterpumptoggle, &TTrain::OnCommand_waterpumptoggle }, + { user_command::waterpumpenable, &TTrain::OnCommand_waterpumpenable }, + { user_command::waterpumpdisable, &TTrain::OnCommand_waterpumpdisable }, + { user_command::watercircuitslinktoggle, &TTrain::OnCommand_watercircuitslinktoggle }, + { user_command::watercircuitslinkenable, &TTrain::OnCommand_watercircuitslinkenable }, + { user_command::watercircuitslinkdisable, &TTrain::OnCommand_watercircuitslinkdisable }, { user_command::convertertoggle, &TTrain::OnCommand_convertertoggle }, { user_command::converterenable, &TTrain::OnCommand_converterenable }, { user_command::converterdisable, &TTrain::OnCommand_converterdisable }, @@ -2078,6 +2093,201 @@ void TTrain::OnCommand_oilpumpdisable( TTrain *Train, command_data const &Comman } } +void TTrain::OnCommand_waterheaterbreakertoggle( TTrain *Train, command_data const &Command ) { + + 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->WaterHeater.breaker ) { + // turn on + OnCommand_waterheaterbreakerclose( Train, Command ); + } + else { + //turn off + OnCommand_waterheaterbreakeropen( Train, Command ); + } + } +} + +void TTrain::OnCommand_waterheaterbreakerclose( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_PRESS ) { + // visual feedback + Train->ggWaterHeaterBreakerButton.UpdateValue( 1.0, Train->dsbSwitch ); + + if( true == Train->mvControlled->WaterHeater.breaker ) { return; } // already enabled + + Train->mvControlled->WaterHeaterBreakerSwitch( true ); + } +} + +void TTrain::OnCommand_waterheaterbreakeropen( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_PRESS ) { + // visual feedback + Train->ggWaterHeaterBreakerButton.UpdateValue( 0.0, Train->dsbSwitch ); + + if( false == Train->mvControlled->WaterHeater.breaker ) { return; } // already enabled + + Train->mvControlled->WaterHeaterBreakerSwitch( false ); + } +} + +void TTrain::OnCommand_waterheatertoggle( TTrain *Train, command_data const &Command ) { + + 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->WaterHeater.is_enabled ) { + // turn on + OnCommand_waterheaterenable( Train, Command ); + } + else { + //turn off + OnCommand_waterheaterdisable( Train, Command ); + } + } +} + +void TTrain::OnCommand_waterheaterenable( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_PRESS ) { + // visual feedback + Train->ggWaterHeaterButton.UpdateValue( 1.0, Train->dsbSwitch ); + + if( true == Train->mvControlled->WaterHeater.is_enabled ) { return; } // already enabled + + Train->mvControlled->WaterHeaterSwitch( true ); + } +} + +void TTrain::OnCommand_waterheaterdisable( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_PRESS ) { + // visual feedback + Train->ggWaterHeaterButton.UpdateValue( 0.0, Train->dsbSwitch ); + + if( false == Train->mvControlled->WaterHeater.is_enabled ) { return; } // already disabled + + Train->mvControlled->WaterHeaterSwitch( false ); + } +} + +void TTrain::OnCommand_waterpumpbreakertoggle( TTrain *Train, command_data const &Command ) { + + 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->WaterPump.breaker ) { + // turn on + OnCommand_waterpumpbreakerclose( Train, Command ); + } + else { + //turn off + OnCommand_waterpumpbreakeropen( Train, Command ); + } + } +} + +void TTrain::OnCommand_waterpumpbreakerclose( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_PRESS ) { + // visual feedback + Train->ggWaterPumpBreakerButton.UpdateValue( 1.0, Train->dsbSwitch ); + + if( true == Train->mvControlled->WaterPump.breaker ) { return; } // already enabled + + Train->mvControlled->WaterPumpBreakerSwitch( true ); + } +} + +void TTrain::OnCommand_waterpumpbreakeropen( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_PRESS ) { + // visual feedback + Train->ggWaterPumpBreakerButton.UpdateValue( 0.0, Train->dsbSwitch ); + + if( false == Train->mvControlled->WaterPump.breaker ) { return; } // already enabled + + Train->mvControlled->WaterPumpBreakerSwitch( false ); + } +} + +void TTrain::OnCommand_waterpumptoggle( TTrain *Train, command_data const &Command ) { + + 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->WaterPump.is_enabled ) { + // turn on + OnCommand_waterpumpenable( Train, Command ); + } + else { + //turn off + OnCommand_waterpumpdisable( Train, Command ); + } + } +} + +void TTrain::OnCommand_waterpumpenable( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_PRESS ) { + // visual feedback + Train->ggWaterPumpButton.UpdateValue( 1.0, Train->dsbSwitch ); + + if( true == Train->mvControlled->WaterPump.is_enabled ) { return; } // already enabled + + Train->mvControlled->WaterPumpSwitch( true ); + } +} + +void TTrain::OnCommand_waterpumpdisable( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_PRESS ) { + // visual feedback + Train->ggWaterPumpButton.UpdateValue( 0.0, Train->dsbSwitch ); + + if( false == Train->mvControlled->WaterPump.is_enabled ) { return; } // already disabled + + Train->mvControlled->WaterPumpSwitch( false ); + } +} + +void TTrain::OnCommand_watercircuitslinktoggle( TTrain *Train, command_data const &Command ) { + + 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->WaterCircuitsLink ) { + // turn on + OnCommand_watercircuitslinkenable( Train, Command ); + } + else { + //turn off + OnCommand_watercircuitslinkdisable( Train, Command ); + } + } +} + +void TTrain::OnCommand_watercircuitslinkenable( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_PRESS ) { + // visual feedback + Train->ggWaterCircuitsLinkButton.UpdateValue( 1.0, Train->dsbSwitch ); + + if( true == Train->mvControlled->WaterCircuitsLink ) { return; } // already enabled + + Train->mvControlled->WaterCircuitsLinkSwitch( true ); + } +} + +void TTrain::OnCommand_watercircuitslinkdisable( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_PRESS ) { + // visual feedback + Train->ggWaterCircuitsLinkButton.UpdateValue( 0.0, Train->dsbSwitch ); + + if( false == Train->mvControlled->WaterCircuitsLink ) { return; } // already disabled + + Train->mvControlled->WaterCircuitsLinkSwitch( false ); + } +} + void TTrain::OnCommand_convertertoggle( TTrain *Train, command_data const &Command ) { if( Command.action == GLFW_PRESS ) { @@ -3938,8 +4148,8 @@ bool TTrain::Update( double const Deltatime ) } } if( m_linebreakerstate == 1 ) { - if( false == mvControlled->Mains ) { - // crude way to catch cases where the main was knocked out and the user is trying to restart it + if( false == ( mvControlled->Mains || mvControlled->dizel_startup ) ) { + // crude way to catch cases where the main was knocked out // because the state of the line breaker isn't changed to match, we need to do it here manually m_linebreakerstate = 0; } @@ -4481,8 +4691,11 @@ bool TTrain::Update( double const Deltatime ) mvControlled->ResistorsFlagCheck() : false ); - btLampkaBezoporowa.Turn( mvControlled->ResistorsFlagCheck() || ( mvControlled->MainCtrlActualPos == 0 ) ); // do EU04 - if( ( mvControlled->Itot != 0 ) + btLampkaBezoporowa.Turn( + ( true == mvControlled->ResistorsFlagCheck() ) + || ( mvControlled->MainCtrlActualPos == 0 ) ); // do EU04 + + if( ( mvControlled->Im != 0 ) || ( mvOccupied->BrakePress > 2 ) || ( mvOccupied->PipePress < 3.6 ) ) { // Ra: czy to jest udawanie działania styczników liniowych? @@ -4721,6 +4934,9 @@ bool TTrain::Update( double const Deltatime ) btLampkaRearRightLight.Turn( ( mvOccupied->iLights[ side::rear ] & light::headlight_right ) != 0 ); btLampkaRearLeftEndLight.Turn( ( mvOccupied->iLights[ side::rear ] & light::redmarker_left ) != 0 ); btLampkaRearRightEndLight.Turn( ( mvOccupied->iLights[ side::rear ] & light::redmarker_right ) != 0 ); + // others + btLampkaMalfunction.Turn( mvControlled->dizel_heat.PA ); + btLampkaMotorBlowers.Turn( mvControlled->RventRot > 0.1 ); } else { // gdy bateria wyłączona @@ -4752,6 +4968,9 @@ bool TTrain::Update( double const Deltatime ) btLampkaRearRightLight.Turn( false ); btLampkaRearLeftEndLight.Turn( false ); btLampkaRearRightEndLight.Turn( false ); + // others + btLampkaMalfunction.Turn( false ); + btLampkaMotorBlowers.Turn( false ); } // McZapkie-080602: obroty (albo translacje) regulatorow @@ -4967,6 +5186,11 @@ bool TTrain::Update( double const Deltatime ) ggCabLightDimButton.Update(); ggBatteryButton.Update(); + ggWaterPumpBreakerButton.Update(); + ggWaterPumpButton.Update(); + ggWaterHeaterBreakerButton.Update(); + ggWaterHeaterButton.Update(); + ggWaterCircuitsLinkButton.Update(); ggFuelPumpButton.Update(); ggOilPumpButton.Update(); //------ @@ -6013,6 +6237,11 @@ void TTrain::clear_cab_controls() ggMainGearStatus.Clear(); ggIgnitionKey.Clear(); + ggWaterPumpBreakerButton.Clear(); + ggWaterPumpButton.Clear(); + ggWaterHeaterBreakerButton.Clear(); + ggWaterHeaterButton.Clear(); + ggWaterCircuitsLinkButton.Clear(); ggFuelPumpButton.Clear(); ggOilPumpButton.Clear(); @@ -6075,6 +6304,10 @@ void TTrain::clear_cab_controls() btLampkaRearLeftEndLight.Clear(); btLampkaRearRightEndLight.Clear(); btCabLight.Clear(); // hunter-171012 + // others + btLampkaMalfunction.Clear(); + btLampkaMotorBlowers.Clear(); + ggLeftLightButton.Clear(); ggRightLightButton.Clear(); ggUpperLightButton.Clear(); @@ -6280,14 +6513,36 @@ void TTrain::set_cab_controls() { ShowNextCurrent ? 1.0 : 0.0 ); + // water pump + ggWaterPumpBreakerButton.PutValue( + mvControlled->WaterPump.breaker ? + 1.0 : + 0.0 ); + ggWaterPumpButton.PutValue( + mvControlled->WaterPump.is_enabled ? + 1.0 : + 0.0 ); + // water heater + ggWaterHeaterBreakerButton.PutValue( + mvControlled->WaterHeater.breaker ? + 1.0 : + 0.0 ); + ggWaterHeaterButton.PutValue( + mvControlled->WaterHeater.is_enabled ? + 1.0 : + 0.0 ); + ggWaterCircuitsLinkButton.PutValue( + mvControlled->WaterCircuitsLink ? + 1.0 : + 0.0 ); // fuel pump ggFuelPumpButton.PutValue( - mvOccupied->FuelPump.is_enabled ? + mvControlled->FuelPump.is_enabled ? 1.0 : 0.0 ); // oil pump ggOilPumpButton.PutValue( - mvOccupied->OilPump.is_enabled ? + mvControlled->OilPump.is_enabled ? 1.0 : 0.0 ); @@ -6329,6 +6584,7 @@ bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int co { "i-no_resistors_b:", btLampkaBezoporowaB }, { "i-highcurrent:", btLampkaWysRozr }, { "i-vent_trim:", btLampkaWentZaluzje }, + { "i-motorblowers:", btLampkaMotorBlowers }, { "i-trainheating:", btLampkaOgrzewanieSkladu }, { "i-security_aware:", btLampkaCzuwaka }, { "i-security_cabsignal:", btLampkaSHP }, @@ -6358,6 +6614,7 @@ bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int co { "i-resistorsb:", btLampkaOporyB }, { "i-contactorsb:", btLampkaStycznB }, { "i-conv_ovldb:", btLampkaNadmPrzetwB }, + { "i-malfunction:", btLampkaMalfunction }, { "i-forward:", btLampkaForward }, { "i-backward:", btLampkaBackward }, { "i-upperlight:", btLampkaUpperLight }, @@ -6396,6 +6653,14 @@ bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int co button.Load(Parser, DynamicObject, DynamicObject->mdKabina); button.AssignBool(bDoors[0] + 3 * i); } +/* + else if( Label == "i-malfunction:" ) { + // generic malfunction indicator + auto &button = Cabine[ Cabindex ].Button( -1 ); // pierwsza wolna gałka + button.Load( Parser, DynamicObject, DynamicObject->mdKabina ); + button.AssignBool( &mvOccupied->dizel_heat.PA ); + } +*/ else { // failed to match the label @@ -6454,6 +6719,11 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con { "converterlocal_sw:", ggConverterLocalButton }, { "converteroff_sw:", ggConverterOffButton }, { "main_sw:", ggMainButton }, + { "waterpumpbreaker_sw:", ggWaterPumpBreakerButton }, + { "waterpump_sw:", ggWaterPumpButton }, + { "waterheaterbreaker_sw:", ggWaterHeaterBreakerButton }, + { "waterheater_sw:", ggWaterHeaterButton }, + { "watercircuitslink_sw:", ggWaterCircuitsLinkButton }, { "fuelpump_sw:", ggFuelPumpButton }, { "oilpump_sw:", ggOilPumpButton }, { "radio_sw:", ggRadioButton }, @@ -6619,7 +6889,25 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con // oil pressure auto &gauge = Cabine[ Cabindex ].Gauge( -1 ); // pierwsza wolna gałka gauge.Load( Parser, DynamicObject, DynamicObject->mdKabina, nullptr ); - gauge.AssignFloat( &mvOccupied->OilPump.pressure_present ); + gauge.AssignFloat( &mvControlled->OilPump.pressure_present ); + } + else if( Label == "oiltemp:" ) { + // oil temperature + auto &gauge = Cabine[ Cabindex ].Gauge( -1 ); // pierwsza wolna gałka + gauge.Load( Parser, DynamicObject, DynamicObject->mdKabina, nullptr ); + gauge.AssignFloat( &mvControlled->dizel_heat.To ); + } + else if( Label == "water1temp:" ) { + // main circuit water temperature + auto &gauge = Cabine[ Cabindex ].Gauge( -1 ); // pierwsza wolna gałka + gauge.Load( Parser, DynamicObject, DynamicObject->mdKabina, nullptr ); + gauge.AssignFloat( &mvControlled->dizel_heat.temperatura1 ); + } + else if( Label == "water2temp:" ) { + // auxiliary circuit water temperature + auto &gauge = Cabine[ Cabindex ].Gauge( -1 ); // pierwsza wolna gałka + gauge.Load( Parser, DynamicObject, DynamicObject->mdKabina, nullptr ); + gauge.AssignFloat( &mvControlled->dizel_heat.temperatura2 ); } // yB - dla drugiej sekcji else if (Label == "hvbcurrent1:") diff --git a/Train.h b/Train.h index c9bc1e7b..60636020 100644 --- a/Train.h +++ b/Train.h @@ -219,6 +219,21 @@ class TTrain static void OnCommand_oilpumptoggle( TTrain *Train, command_data const &Command ); static void OnCommand_oilpumpenable( TTrain *Train, command_data const &Command ); static void OnCommand_oilpumpdisable( TTrain *Train, command_data const &Command ); + static void OnCommand_waterheaterbreakertoggle( TTrain *Train, command_data const &Command ); + static void OnCommand_waterheaterbreakerclose( TTrain *Train, command_data const &Command ); + static void OnCommand_waterheaterbreakeropen( TTrain *Train, command_data const &Command ); + static void OnCommand_waterheatertoggle( TTrain *Train, command_data const &Command ); + static void OnCommand_waterheaterenable( TTrain *Train, command_data const &Command ); + static void OnCommand_waterheaterdisable( TTrain *Train, command_data const &Command ); + static void OnCommand_waterpumpbreakertoggle( TTrain *Train, command_data const &Command ); + static void OnCommand_waterpumpbreakerclose( TTrain *Train, command_data const &Command ); + static void OnCommand_waterpumpbreakeropen( TTrain *Train, command_data const &Command ); + static void OnCommand_waterpumptoggle( TTrain *Train, command_data const &Command ); + static void OnCommand_waterpumpenable( TTrain *Train, command_data const &Command ); + static void OnCommand_waterpumpdisable( TTrain *Train, command_data const &Command ); + static void OnCommand_watercircuitslinktoggle( TTrain *Train, command_data const &Command ); + static void OnCommand_watercircuitslinkenable( TTrain *Train, command_data const &Command ); + static void OnCommand_watercircuitslinkdisable( TTrain *Train, command_data const &Command ); static void OnCommand_convertertoggle( TTrain *Train, command_data const &Command ); static void OnCommand_converterenable( TTrain *Train, command_data const &Command ); static void OnCommand_converterdisable( TTrain *Train, command_data const &Command ); @@ -408,6 +423,11 @@ public: // reszta może by?publiczna TGauge ggSignallingButton; TGauge ggDoorSignallingButton; + TGauge ggWaterPumpBreakerButton; // water pump breaker switch + TGauge ggWaterPumpButton; // water pump switch + TGauge ggWaterHeaterBreakerButton; // water heater breaker switch + TGauge ggWaterHeaterButton; // water heater switch + TGauge ggWaterCircuitsLinkButton; TGauge ggFuelPumpButton; // fuel pump switch TGauge ggOilPumpButton; // fuel pump switch @@ -468,7 +488,6 @@ public: // reszta może by?publiczna TButton btLampkaBrakeProfileG; // cargo train brake acting speed TButton btLampkaBrakeProfileP; // passenger train brake acting speed TButton btLampkaBrakeProfileR; // rapid brake acting speed - // KURS90 TButton btLampkaBoczniki; TButton btLampkaMaxSila; @@ -491,6 +510,9 @@ public: // reszta może by?publiczna TButton btLampkaRearRightLight; TButton btLampkaRearLeftEndLight; TButton btLampkaRearRightEndLight; + // other + TButton btLampkaMalfunction; + TButton btLampkaMotorBlowers; TButton btCabLight; // hunter-171012: lampa oswietlajaca kabine // Ra 2013-12: wirtualne "lampki" do odbijania na haslerze w PoKeys diff --git a/command.cpp b/command.cpp index 935e2d4c..c7b0073f 100644 --- a/command.cpp +++ b/command.cpp @@ -63,6 +63,21 @@ commanddescription_sequence Commands_descriptions = { { "reverserforward", command_target::vehicle }, { "reverserneutral", command_target::vehicle }, { "reverserbackward", command_target::vehicle }, + { "waterpumpbreakertoggle", command_target::vehicle }, + { "waterpumpbreakerclose", command_target::vehicle }, + { "waterpumpbreakeropen", command_target::vehicle }, + { "waterpumptoggle", command_target::vehicle }, + { "waterpumpenable", command_target::vehicle }, + { "waterpumpdisable", command_target::vehicle }, + { "waterheaterbreakertoggle", command_target::vehicle }, + { "waterheaterbreakerclose", command_target::vehicle }, + { "waterheaterbreakeropen", command_target::vehicle }, + { "waterheatertoggle", command_target::vehicle }, + { "waterheaterenable", command_target::vehicle }, + { "waterheaterdisable", command_target::vehicle }, + { "watercircuitslinktoggle", command_target::vehicle }, + { "watercircuitslinkenable", command_target::vehicle }, + { "watercircuitslinkdisable", command_target::vehicle }, { "fuelpumptoggle", command_target::vehicle }, { "fuelpumpenable", command_target::vehicle }, { "fuelpumpdisable", command_target::vehicle }, diff --git a/command.h b/command.h index d34f936b..be90b1c8 100644 --- a/command.h +++ b/command.h @@ -57,6 +57,21 @@ enum class user_command { reverserforward, reverserneutral, reverserbackward, + waterpumpbreakertoggle, + waterpumpbreakerclose, + waterpumpbreakeropen, + waterpumptoggle, + waterpumpenable, + waterpumpdisable, + waterheaterbreakertoggle, + waterheaterbreakerclose, + waterheaterbreakeropen, + waterheatertoggle, + waterheaterenable, + waterheaterdisable, + watercircuitslinktoggle, + watercircuitslinkenable, + watercircuitslinkdisable, fuelpumptoggle, fuelpumpenable, fuelpumpdisable, diff --git a/keyboardinput.cpp b/keyboardinput.cpp index f4f72199..4f60b08f 100644 --- a/keyboardinput.cpp +++ b/keyboardinput.cpp @@ -255,6 +255,36 @@ keyboard_input::default_bindings() { { -1 }, // reverserbackward { -1 }, + // waterpumpbreakertoggle + { GLFW_KEY_W | keymodifier::control }, + // waterpumpbreakerclose + { -1 }, + // waterpumpbreakeropen + { -1 }, + // waterpumptoggle + { GLFW_KEY_W }, + // waterpumpenable + { -1 }, + // waterpumpdisable + { -1 }, + // waterheaterbreakertoggle + { GLFW_KEY_W | keymodifier::control | keymodifier::shift }, + // waterheaterbreakerclose + { -1 }, + // waterheaterbreakeropen + { -1 }, + // waterheatertoggle + { GLFW_KEY_W | keymodifier::shift }, + // waterheaterenable + { -1 }, + // waterheaterdisable + { -1 }, + // watercircuitslinktoggle + { GLFW_KEY_H | keymodifier::shift }, + // watercircuitslinkenable + { -1 }, + // watercircuitslinkdisable + { -1 }, // fuelpumptoggle { GLFW_KEY_F }, // fuelpumpenable, diff --git a/mouseinput.cpp b/mouseinput.cpp index 5b2e6826..5e4ea8ee 100644 --- a/mouseinput.cpp +++ b/mouseinput.cpp @@ -243,6 +243,18 @@ mouse_input::default_bindings() { { "maxcurrent_sw:", { user_command::motoroverloadrelaythresholdtoggle, user_command::none } }, + { "waterpumpbreaker_sw:", { + user_command::waterpumpbreakertoggle, + user_command::none } }, + { "waterpump_sw:", { + user_command::waterpumptoggle, + user_command::none } }, + { "waterheaterbreaker_sw:", { + user_command::waterheaterbreakertoggle, + user_command::none } }, + { "waterheater_sw:", { + user_command::waterheatertoggle, + user_command::none } }, { "fuelpump_sw:", { user_command::fuelpumptoggle, user_command::none } }, diff --git a/translation.h b/translation.h index 2c1fd25b..d60c3fef 100644 --- a/translation.h +++ b/translation.h @@ -29,6 +29,11 @@ static std::unordered_map m_cabcontrols = { { "brakeprofileg_sw:", "brake acting speed: cargo" }, { "brakeprofiler_sw:", "brake acting speed: rapid" }, { "maxcurrent_sw:", "motor overload relay threshold" }, + { "waterpump_sw:", "water pump" }, + { "waterpumpbreaker_sw:", "water pump breaker" }, + { "waterheater_sw:", "water heater" }, + { "waterheaterbreaker_sw:", "water heater breaker" }, + { "watercircuitslink_sw:", "water circuits link" }, { "fuelpump_sw:", "fuel pump" }, { "oilpump_sw:", "oil pump" }, { "main_off_bt:", "line breaker" }, diff --git a/uilayer.cpp b/uilayer.cpp index b0a597b3..21b71d02 100644 --- a/uilayer.cpp +++ b/uilayer.cpp @@ -423,7 +423,9 @@ ui_layer::update() { uitextline2 += ( vehicle->MoverParameters->PantRearUp ? ( vehicle->MoverParameters->PantRearVolt > 0.0 ? "O" : "o" ) : "." ); uitextline2 += ( vehicle->MoverParameters->PantFrontUp ? ( vehicle->MoverParameters->PantFrontVolt > 0.0 ? "P" : "p" ) : "." ); uitextline2 += ( vehicle->MoverParameters->PantPressLockActive ? "!" : ( vehicle->MoverParameters->PantPressSwitchActive ? "*" : "." ) ); - uitextline2 += ( vehicle->MoverParameters->FuelPump.is_enabled ? ( vehicle->MoverParameters->FuelPump.is_active ? "F" : "f" ) : "." ); + uitextline2 += ( vehicle->MoverParameters->WaterPump.is_active ? "W" : ( false == vehicle->MoverParameters->WaterPump.breaker ? "-" : ( vehicle->MoverParameters->WaterPump.is_enabled ? "w" : "." ) ) ); + uitextline2 += ( true == vehicle->MoverParameters->WaterHeater.is_damaged ? "!" : ( vehicle->MoverParameters->WaterHeater.is_active ? "H" : ( false == vehicle->MoverParameters->WaterHeater.breaker ? "-" : ( vehicle->MoverParameters->WaterHeater.is_enabled ? "h" : "." ) ) ) ); + uitextline2 += ( vehicle->MoverParameters->FuelPump.is_active ? "F" : ( vehicle->MoverParameters->FuelPump.is_enabled ? "f" : "." ) ); uitextline2 += ( vehicle->MoverParameters->OilPump.is_active ? "O" : ( vehicle->MoverParameters->OilPump.is_enabled ? "o" : "." ) ); uitextline2 += ( false == vehicle->MoverParameters->ConverterAllowLocal ? "-" : ( vehicle->MoverParameters->ConverterAllow ? ( vehicle->MoverParameters->ConverterFlag ? "X" : "x" ) : "." ) ); uitextline2 += ( vehicle->MoverParameters->ConvOvldFlag ? "!" : "." ); @@ -673,7 +675,8 @@ ui_layer::update() { + ", PM=" + to_string( vehicle->MoverParameters->WheelFlat, 1 ) + " mm; enrot=" + to_string( vehicle->MoverParameters->enrot * 60, 0 ) + " tmrot=" + to_string( std::abs( vehicle->MoverParameters->nrot ) * vehicle->MoverParameters->Transmision.Ratio * 60, 0 ) - + "; ventrot=" + to_string( vehicle->MoverParameters->RventRot * 60, 1 ); + + "; ventrot=" + to_string( vehicle->MoverParameters->RventRot * 60, 1 ) + + "; fanrot=" + to_string( vehicle->MoverParameters->dizel_heat.rpmw, 1 ) + ", " + to_string( vehicle->MoverParameters->dizel_heat.rpmw2, 1 ); uitextline2 = "HamZ=" + to_string( vehicle->MoverParameters->fBrakeCtrlPos, 2 ) @@ -702,7 +705,12 @@ ui_layer::update() { /* uitextline2 += " eAngle=" + to_string( std::cos( vehicle->MoverParameters->eAngle ), 2 ); */ - uitextline2 += " oilP=" + to_string( vehicle->MoverParameters->OilPump.pressure_present, 3 ); + uitextline2 += "; oilP=" + to_string( vehicle->MoverParameters->OilPump.pressure_present, 3 ); + uitextline2 += " oilT=" + to_string( vehicle->MoverParameters->dizel_heat.To, 2 ); + uitextline2 += "; waterT=" + to_string( vehicle->MoverParameters->dizel_heat.temperatura1, 2 ); + uitextline2 += ( vehicle->MoverParameters->WaterCircuitsLink ? "-" : "|" ); + uitextline2 += to_string( vehicle->MoverParameters->dizel_heat.temperatura2, 2 ); + uitextline2 += "; engineT=" + to_string( vehicle->MoverParameters->dizel_heat.Ts, 2 ); uitextline3 = "cyl.ham. " + to_string( vehicle->MoverParameters->BrakePress, 2 ) From 0cb90a4e9ae7eee20fd0f1421032e7ebb29b0529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=B3lik=20Uszasty?= Date: Sun, 15 Apr 2018 14:58:51 +0200 Subject: [PATCH 2/6] Improved speed controller - speed can be selected by SecondControlPosition --- Driver.cpp | 20 ++++++++++++++++++-- Driver.h | 1 + McZapkie/MOVER.h | 3 +++ McZapkie/Mover.cpp | 35 ++++++++++++++++++++++++++++++++--- 4 files changed, 54 insertions(+), 5 deletions(-) diff --git a/Driver.cpp b/Driver.cpp index eb44fdf0..1c2522ef 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -2728,13 +2728,14 @@ bool TController::IncSpeed() { OK = mvControlling->IncMainCtrl(std::max(1,mvOccupied->MainCtrlPosNo/10)); //tutaj jeszcze powinien być tempomat - mvControlling->IncScndCtrl(1); + double SpeedCntrl = VelDesired; if (fProximityDist < 50) { SpeedCntrl = std::min(SpeedCntrl, VelNext); } - mvControlling->RunCommand("SpeedCntrl", VelDesired, mvControlling->CabNo); + this->SpeedCntrl(SpeedCntrl); + } break; case WheelsDriven: @@ -3011,6 +3012,21 @@ void TController::SpeedSet() } }; +void TController::SpeedCntrl(double DesiredSpeed) +{ + if (mvControlling->ScndCtrlPosNo == 1) + { + mvControlling->IncScndCtrl(1); + mvControlling->RunCommand("SpeedCntrl", DesiredSpeed, mvControlling->CabNo); + } + else if (mvControlling->ScndCtrlPosNo > 1) + { + int DesiredPos = 1 + mvControlling->ScndCtrlPosNo * ((DesiredSpeed - 1.0) / mvControlling->Vmax); + while (mvControlling->ScndCtrlPos > DesiredPos) mvControlling->DecScndCtrl(1); + while (mvControlling->ScndCtrlPos < DesiredPos) mvControlling->IncScndCtrl(1); + } +}; + // otwieranie/zamykanie drzwi w składzie albo (tylko AI) EZT void TController::Doors( bool const Open, int const Side ) { diff --git a/Driver.h b/Driver.h index 214df307..95277696 100644 --- a/Driver.h +++ b/Driver.h @@ -304,6 +304,7 @@ private: bool IncSpeed(); bool DecSpeed(bool force = false); void SpeedSet(); + void SpeedCntrl(double DesiredSpeed); void Doors(bool const Open, int const Side = 0); // returns true if any vehicle in the consist has an open door bool doors_open() const; diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 97231ad9..a67278b5 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -940,6 +940,7 @@ public: double PlatformMaxShift = 0.5; /*wysuniecie stopnia*/ int PlatformOpenMethod = 1; /*sposob animacji stopnia*/ bool ScndS = false; /*Czy jest bocznikowanie na szeregowej*/ + double SpeedCtrlDelay = 2; /*opoznienie dzialania tempomatu z wybieralna predkoscia*/ /*--sekcja zmiennych*/ /*--opis konkretnego egzemplarza taboru*/ TLocation Loc; //pozycja pojazdów do wyznaczenia odległości pomiędzy sprzęgami @@ -1127,6 +1128,8 @@ public: /*- zmienne dla lokomotyw z silnikami indukcyjnymi -*/ double eimv[21]; static std::vector const eimv_labels; + double SpeedCtrlTimer = 0; /*zegar dzialania tempomatu z wybieralna predkoscia*/ + double NewSpeed = 0; /*nowa predkosc do zadania*/ /*-zmienne dla drezyny*/ double PulseForce = 0.0; /*przylozona sila*/ diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index b340ca79..9a5200f0 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -2002,7 +2002,7 @@ bool TMoverParameters::IncScndCtrl(int CtrlSpeed) if (LastRelayTime > CtrlDelay) LastRelayTime = 0; - if ((OK) && (EngineType == ElectricInductionMotor)) + if ((OK) && (EngineType == ElectricInductionMotor) && (ScndCtrlPosNo == 1)) { // NOTE: round() already adds 0.5, are the ones added here as well correct? if ((Vmax < 250)) @@ -2060,7 +2060,7 @@ bool TMoverParameters::DecScndCtrl(int CtrlSpeed) if (LastRelayTime > CtrlDownDelay) LastRelayTime = 0; - if ((OK) && (EngineType == ElectricInductionMotor)) + if ((OK) && (EngineType == ElectricInductionMotor) && (ScndCtrlPosNo == 1)) { ScndCtrlActualPos = 0; SendCtrlToNext("SpeedCntrl", ScndCtrlActualPos, CabNo); @@ -4992,6 +4992,32 @@ double TMoverParameters::TractionForce(double dt) } if( true == Mains ) { + //tempomat + + if (ScndCtrlPosNo > 1) + { + if (ScndCtrlPos != NewSpeed) + { + + SpeedCtrlTimer = 0; + NewSpeed = ScndCtrlPos; + } + else + { + SpeedCtrlTimer += dt; + if (SpeedCtrlTimer > SpeedCtrlDelay) + { + int NewSCAP = (Vmax < 250 ? 1 : 0.5) * (float)ScndCtrlPos / (float)ScndCtrlPosNo * Vmax; + if (NewSCAP != ScndCtrlActualPos) + { + ScndCtrlActualPos = NewSCAP; + SendCtrlToNext("SpeedCntrl", ScndCtrlActualPos, CabNo); + } + } + } + } + + dtrans = Hamulec->GetEDBCP(); if (((DoorLeftOpened) || (DoorRightOpened))) DynamicBrakeFlag = true; @@ -5031,7 +5057,7 @@ double TMoverParameters::TractionForce(double dt) eimv[eimv_Fzad] = PosRatio; if ((Flat) && (eimc[eimc_p_F0] * eimv[eimv_Fful] > 0)) PosRatio = Min0R(PosRatio * eimc[eimc_p_F0] / eimv[eimv_Fful], 1); - if (ScndCtrlActualPos > 0) + if (ScndCtrlActualPos > 0) //speed control if (Vmax < 250) PosRatio = Min0R(PosRatio, Max0R(-1, 0.5 * (ScndCtrlActualPos - Vel))); else @@ -8093,6 +8119,9 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) { extract_value( StopBrakeDecc, "SBD", line, "" ); + // speed control + extract_value( SpeedCtrlDelay, "SpeedCtrlDelay", line, "" ); + // converter { std::map starts { From db9d34aa6b8719e9e7d3fddf48886c9d6cc6a510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=B3lik=20Uszasty?= Date: Sun, 15 Apr 2018 17:48:09 +0200 Subject: [PATCH 3/6] Fix for diesel engine work with fast gear change or no direction set --- McZapkie/Mover.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 9a5200f0..3fbbf0c3 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -6148,7 +6148,7 @@ double TMoverParameters::dizel_Momentum(double dizel_fill, double n, double dt) double Moment = 0, enMoment = 0, gearMoment = 0, eps = 0, newn = 0, friction = 0, neps = 0; double TorqueH = 0, TorqueL = 0, TorqueC = 0; n = n * CabNo; - if (MotorParam[ScndCtrlActualPos].mIsat < 0.001) + if ((MotorParam[ScndCtrlActualPos].mIsat < 0.001)||(ActiveDir == 0)) n = enrot; friction = dizel_engagefriction; hydro_TC_nIn = enrot; //wal wejsciowy przetwornika momentu @@ -6260,7 +6260,7 @@ double TMoverParameters::dizel_Momentum(double dizel_fill, double n, double dt) } eps = enMoment / dizel_AIM; newn = enrot + eps * dt; - if ((newn - n)*(enrot - dizel_n_old) < 0) //przejscie przez zero - slizgalo sie i przestało + if (((newn - n)*(enrot - dizel_n_old) < 0)&&(TorqueC>0.1)) //przejscie przez zero - slizgalo sie i przestało newn = n; if ((newn * enrot <= 0) && (eps * enrot < 0)) //przejscie przez zero obrotow newn = 0; From bb87a63eed9d23e44a54cf7dbb0fba07775f5ad0 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Wed, 18 Apr 2018 15:04:04 +0200 Subject: [PATCH 4/6] varying scale cab control animation types, basic shunt mode power slider cab control, minor ai logic tweaks, minor bug fixes --- Driver.cpp | 22 +++- EU07.cpp | 4 +- Gauge.cpp | 275 ++++++++++++++++++++++++--------------------- Gauge.h | 38 ++++--- McZapkie/Mover.cpp | 23 ++-- Train.cpp | 20 +++- Train.h | 1 + 7 files changed, 224 insertions(+), 159 deletions(-) diff --git a/Driver.cpp b/Driver.cpp index 1c2522ef..6af63776 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -2223,13 +2223,15 @@ bool TController::PrepareEngine() voltfront = true; } } + auto workingtemperature { true }; if (AIControllFlag) { // część wykonawcza dla sterowania przez komputer mvOccupied->BatterySwitch( true ); if( ( mvControlling->EngineType == DieselElectric ) || ( mvControlling->EngineType == DieselEngine ) ) { mvControlling->OilPumpSwitch( true ); - if( true == UpdateHeating() ) { + workingtemperature = UpdateHeating(); + if( true == workingtemperature ) { mvControlling->FuelPumpSwitch( true ); } } @@ -2339,7 +2341,7 @@ bool TController::PrepareEngine() else OK = false; - OK = OK && (mvOccupied->ActiveDir != 0) && (mvControlling->CompressorAllow); + OK = OK && (mvOccupied->ActiveDir != 0) && (mvControlling->CompressorAllow) && (workingtemperature); if (OK) { if (eStopReason == stopSleep) // jeśli dotychczas spał @@ -2378,6 +2380,7 @@ bool TController::ReleaseEngine() mvControlling->PantFront(false); mvControlling->PantRear(false); } + // line breaker OK = mvControlling->MainSwitch(false); } else @@ -2410,7 +2413,20 @@ bool TController::ReleaseEngine() eAction = actSleep; //śpi (wygaszony) if (AIControllFlag) { - Lights(0, 0); // gasimy światła + if( ( mvControlling->EngineType == DieselElectric ) + || ( mvControlling->EngineType == DieselEngine ) ) { + // heating/cooling subsystem + mvControlling->WaterHeaterSwitch( false ); + // optionally turn off the water pump as well + if( mvControlling->WaterPump.start_type != start::battery ) { + mvControlling->WaterPumpSwitch( false ); + } + // fuel and oil subsystems + mvControlling->FuelPumpSwitch( false ); + mvControlling->OilPumpSwitch( false ); + } + // gasimy światła + Lights(0, 0); mvOccupied->BatterySwitch(false); } OrderNext(Wait_for_orders); //żeby nie próbował coś robić dalej diff --git a/EU07.cpp b/EU07.cpp index f1f1c90b..ff276ba6 100644 --- a/EU07.cpp +++ b/EU07.cpp @@ -412,7 +412,9 @@ int main(int argc, char *argv[]) return -1; } - audio::renderer.init(); + if( Global.bSoundEnabled ) { + Global.bSoundEnabled &= audio::renderer.init(); + } input::Keyboard.init(); input::Mouse.init(); diff --git a/Gauge.cpp b/Gauge.cpp index 75bf2e05..cb1b8440 100644 --- a/Gauge.cpp +++ b/Gauge.cpp @@ -21,46 +21,54 @@ http://mozilla.org/MPL/2.0/. #include "logs.h" #include "renderer.h" -void TGauge::Init(TSubModel *NewSubModel, TGaugeType eNewType, double fNewScale, double fNewOffset, double fNewFriction, double fNewValue) +void TGauge::Init(TSubModel *Submodel, TGaugeType Type, float Scale, float Offset, float Friction, float Value, float const Endvalue, float const Endscale, bool const Interpolatescale ) { // ustawienie parametrów animacji submodelu - if (NewSubModel) { - // warunek na wszelki wypadek, gdyby się submodel nie podłączył - fFriction = fNewFriction; - fValue = fNewValue; - fOffset = fNewOffset; - fScale = fNewScale; - SubModel = NewSubModel; - eType = eNewType; - if (eType == gt_Digital) { + SubModel = Submodel; + m_value = Value; + m_type = Type; + m_scale = Scale; + m_offset = Offset; + m_friction = Friction; + m_interpolatescale = Interpolatescale; + m_endvalue = Endvalue; + m_endscale = Endscale; - TSubModel *sm = SubModel->ChildGet(); - do { - // pętla po submodelach potomnych i obracanie ich o kąt zależy od cyfry w (fValue) - if (sm->pName.size()) - { // musi mieć niepustą nazwę - if (sm->pName[0] >= '0') - if (sm->pName[0] <= '9') - sm->WillBeAnimated(); // wyłączenie optymalizacji - } - sm = sm->NextGet(); - } while (sm); - } - else // a banan może być z optymalizacją? - NewSubModel->WillBeAnimated(); // wyłączenie ignowania jedynkowego transformu - // pass submodel location to defined sounds - auto const offset { model_offset() }; - m_soundfxincrease.offset( offset ); - m_soundfxdecrease.offset( offset ); - for( auto &soundfxrecord : m_soundfxvalues ) { - soundfxrecord.second.offset( offset ); - } + if( Submodel == nullptr ) { + // warunek na wszelki wypadek, gdyby się submodel nie podłączył + return; + } + + if( m_type == gt_Digital ) { + + TSubModel *sm = SubModel->ChildGet(); + do { + // pętla po submodelach potomnych i obracanie ich o kąt zależy od cyfry w (fValue) + if( sm->pName.size() ) { // musi mieć niepustą nazwę + if( sm->pName[ 0 ] >= '0' ) + if( sm->pName[ 0 ] <= '9' ) + sm->WillBeAnimated(); // wyłączenie optymalizacji + } + sm = sm->NextGet(); + } while( sm ); + } + else // a banan może być z optymalizacją? + Submodel->WillBeAnimated(); // wyłączenie ignowania jedynkowego transformu + // pass submodel location to defined sounds + auto const offset{ model_offset() }; + m_soundfxincrease.offset( offset ); + m_soundfxdecrease.offset( offset ); + for( auto &soundfxrecord : m_soundfxvalues ) { + soundfxrecord.second.offset( offset ); } }; bool TGauge::Load( cParser &Parser, TDynamicObject const *Owner, TModel3d *md1, TModel3d *md2, double mul ) { std::string submodelname, gaugetypename; - double scale, offset, friction; + float scale, endscale, endvalue, offset, friction; + endscale = -1; + endvalue = -1; + bool interpolatescale { false }; Parser.getTokens(); if( Parser.peek() != "{" ) { @@ -72,6 +80,14 @@ bool TGauge::Load( cParser &Parser, TDynamicObject const *Owner, TModel3d *md1, >> scale >> offset >> friction; + if( ( gaugetypename == "rotvar" ) + || ( gaugetypename == "movvar" ) ) { + interpolatescale = true; + Parser.getTokens( 2, false ); + Parser + >> endvalue + >> endscale; + } } else { // new, block type config @@ -83,6 +99,14 @@ bool TGauge::Load( cParser &Parser, TDynamicObject const *Owner, TModel3d *md1, >> scale >> offset >> friction; + if( ( gaugetypename == "rotvar" ) + || ( gaugetypename == "movvar" ) ) { + interpolatescale = true; + Parser.getTokens( 2, false ); + Parser + >> endvalue + >> endscale; + } // new, variable length section while( true == Load_mapping( Parser ) ) { ; // all work done by while() @@ -97,11 +121,10 @@ bool TGauge::Load( cParser &Parser, TDynamicObject const *Owner, TModel3d *md1, } scale *= mul; - TSubModel *submodel = md1->GetFromName( submodelname ); - if( scale == 0.0 ) { - ErrorLog( "Bad model: scale of 0.0 defined for sub-model \"" + submodelname + "\" in 3d model \"" + md1->NameGet() + "\". Forcing scale of 1.0 to prevent division by 0", logtype::model ); - scale = 1.0; + if( interpolatescale ) { + endscale *= mul; } + TSubModel *submodel = md1->GetFromName( submodelname ); if (submodel) // jeśli nie znaleziony md2 = nullptr; // informacja, że znaleziony else if (md2) // a jest podany drugi model (np. zewnętrzny) @@ -111,7 +134,10 @@ bool TGauge::Load( cParser &Parser, TDynamicObject const *Owner, TModel3d *md1, } std::map gaugetypes { + { "rot", gt_Rotate }, + { "rotvar", gt_Rotate }, { "mov", gt_Move }, + { "movvar", gt_Move }, { "wip", gt_Wiper }, { "dgt", gt_Digital } }; @@ -119,8 +145,9 @@ bool TGauge::Load( cParser &Parser, TDynamicObject const *Owner, TModel3d *md1, auto const type = ( lookup != gaugetypes.end() ? lookup->second : - gt_Rotate ); - Init(submodel, type, scale, offset, friction); + gt_Unknown ); + + Init( submodel, type, scale, offset, friction, 0, endvalue, endscale, interpolatescale ); return md2 != nullptr; // true, gdy podany model zewnętrzny, a w kabinie nie było }; @@ -151,51 +178,27 @@ TGauge::Load_mapping( cParser &Input ) { return true; // return value marks a key: value pair was extracted, nothing about whether it's recognized } -void TGauge::PermIncValue(double fNewDesired) -{ - fDesiredValue = fDesiredValue + fNewDesired * fScale + fOffset; - if (fDesiredValue - fOffset > 360 / fScale) - { - fDesiredValue = fDesiredValue - (360 / fScale); - fValue = fValue - (360 / fScale); - } -}; - -void TGauge::IncValue(double fNewDesired) -{ // używane tylko dla uniwersali - fDesiredValue = fDesiredValue + fNewDesired * fScale + fOffset; - if (fDesiredValue > fScale + fOffset) - fDesiredValue = fScale + fOffset; -}; - -void TGauge::DecValue(double fNewDesired) -{ // używane tylko dla uniwersali - fDesiredValue = fDesiredValue - fNewDesired * fScale + fOffset; - if (fDesiredValue < 0) - fDesiredValue = 0; -}; - void -TGauge::UpdateValue( double fNewDesired ) { +TGauge::UpdateValue( float fNewDesired ) { return UpdateValue( fNewDesired, nullptr ); } void -TGauge::UpdateValue( double fNewDesired, sound_source &Fallbacksound ) { +TGauge::UpdateValue( float fNewDesired, sound_source &Fallbacksound ) { return UpdateValue( fNewDesired, &Fallbacksound ); } // ustawienie wartości docelowej. plays provided fallback sound, if no sound was defined in the control itself void -TGauge::UpdateValue( double fNewDesired, sound_source *Fallbacksound ) { +TGauge::UpdateValue( float fNewDesired, sound_source *Fallbacksound ) { auto const desiredtimes100 = static_cast( std::round( 100.0 * fNewDesired ) ); - if( static_cast( std::round( 100.0 * ( fDesiredValue - fOffset ) / fScale ) ) == desiredtimes100 ) { + if( desiredtimes100 == static_cast( 100.0 * m_targetvalue ) ) { return; } - fDesiredValue = fNewDesired * fScale + fOffset; + m_targetvalue = fNewDesired; // if there's any sound associated with new requested value, play it // check value-specific table first... if( desiredtimes100 % 100 == 0 ) { @@ -224,115 +227,137 @@ TGauge::UpdateValue( double fNewDesired, sound_source *Fallbacksound ) { } }; -void TGauge::PutValue(double fNewDesired) +void TGauge::PutValue(float fNewDesired) { // McZapkie-281102: natychmiastowe wpisanie wartosci - fDesiredValue = fNewDesired * fScale + fOffset; - fValue = fDesiredValue; + m_targetvalue = fNewDesired; + m_value = m_targetvalue; }; -double TGauge::GetValue() const { +float TGauge::GetValue() const { // we feed value in range 0-1 so we should be getting it reported in the same range - return ( fValue - fOffset ) / fScale; + return m_value; } -double TGauge::GetDesiredValue() const { +float TGauge::GetDesiredValue() const { // we feed value in range 0-1 so we should be getting it reported in the same range - return ( fDesiredValue - fOffset ) / fScale; + return m_targetvalue; } void TGauge::Update() { - if( fValue != fDesiredValue ) { + if( m_value != m_targetvalue ) { float dt = Timer::GetDeltaTime(); - if( ( fFriction > 0 ) && ( dt < 0.5 * fFriction ) ) { + if( ( m_friction > 0 ) && ( dt < 0.5 * m_friction ) ) { // McZapkie-281102: zabezpieczenie przed oscylacjami dla dlugich czasow - fValue += dt * ( fDesiredValue - fValue ) / fFriction; - if( std::abs( fDesiredValue - fValue ) <= 0.0001 ) { + m_value += dt * ( m_targetvalue - m_value ) / m_friction; + if( std::abs( m_targetvalue - m_value ) <= 0.0001 ) { // close enough, we can stop updating the model - fValue = fDesiredValue; // set it exactly as requested just in case it matters + m_value = m_targetvalue; // set it exactly as requested just in case it matters } } else { - fValue = fDesiredValue; + m_value = m_targetvalue; } } if( SubModel ) { // warunek na wszelki wypadek, gdyby się submodel nie podłączył - TSubModel *sm; - switch (eType) - { - case gt_Rotate: - SubModel->SetRotate(float3(0, 1, 0), fValue * 360.0); - break; - case gt_Move: - SubModel->SetTranslate(float3(0, 0, fValue)); - break; - case gt_Wiper: - SubModel->SetRotate(float3(0, 1, 0), fValue * 360.0); - sm = SubModel->ChildGet(); - if (sm) - { - sm->SetRotate(float3(0, 1, 0), fValue * 360.0); - sm = sm->ChildGet(); - if (sm) - sm->SetRotate(float3(0, 1, 0), fValue * 360.0); + switch (m_type) { + case gt_Rotate: { + SubModel->SetRotate( float3( 0, 1, 0 ), GetScaledValue() * 360.0 ); + break; } - break; - case gt_Digital: // Ra 2014-07: licznik cyfrowy - sm = SubModel->ChildGet(); -/* std::string n = FormatFloat( "0000000000", floor( fValue ) ); // na razie tak trochę bez sensu -*/ std::string n( "000000000" + std::to_string( static_cast( std::floor( fValue ) ) ) ); - if( n.length() > 10 ) { n.erase( 0, n.length() - 10 ); } // also dumb but should work for now - do - { // pętla po submodelach potomnych i obracanie ich o kąt zależy od cyfry w (fValue) - if( sm->pName.size() ) { - // musi mieć niepustą nazwę - if( ( sm->pName[ 0 ] >= '0' ) - && ( sm->pName[ 0 ] <= '9' ) ) { - - sm->SetRotate( - float3( 0, 1, 0 ), - -36.0 * ( n[ '0' + 9 - sm->pName[ 0 ] ] - '0' ) ); - } + case gt_Move: { + SubModel->SetTranslate( float3( 0, 0, GetScaledValue() ) ); + break; + } + case gt_Wiper: { + auto const scaledvalue { GetScaledValue() }; + SubModel->SetRotate( float3( 0, 1, 0 ), scaledvalue * 360.0 ); + auto *sm = SubModel->ChildGet(); + if( sm ) { + sm->SetRotate( float3( 0, 1, 0 ), scaledvalue * 360.0 ); + sm = sm->ChildGet(); + if( sm ) + sm->SetRotate( float3( 0, 1, 0 ), scaledvalue * 360.0 ); } - sm = sm->NextGet(); - } while (sm); - break; + break; + } + case gt_Digital: { + // Ra 2014-07: licznik cyfrowy + auto *sm = SubModel->ChildGet(); +/* std::string n = FormatFloat( "0000000000", floor( fValue ) ); // na razie tak trochę bez sensu +*/ std::string n( "000000000" + std::to_string( static_cast( std::floor( GetScaledValue() ) ) ) ); + if( n.length() > 10 ) { n.erase( 0, n.length() - 10 ); } // also dumb but should work for now + do { // pętla po submodelach potomnych i obracanie ich o kąt zależy od cyfry w (fValue) + if( sm->pName.size() ) { + // musi mieć niepustą nazwę + if( ( sm->pName[ 0 ] >= '0' ) + && ( sm->pName[ 0 ] <= '9' ) ) { + + sm->SetRotate( + float3( 0, 1, 0 ), + -36.0 * ( n[ '0' + 9 - sm->pName[ 0 ] ] - '0' ) ); + } + } + sm = sm->NextGet(); + } while( sm ); + break; + } + default: { + break; + } } } }; void TGauge::AssignFloat(float *fValue) { - cDataType = 'f'; + m_datatype = 'f'; fData = fValue; }; + void TGauge::AssignDouble(double *dValue) { - cDataType = 'd'; + m_datatype = 'd'; dData = dValue; }; + void TGauge::AssignInt(int *iValue) { - cDataType = 'i'; + m_datatype = 'i'; iData = iValue; }; + void TGauge::UpdateValue() { // ustawienie wartości docelowej z parametru - switch (cDataType) + switch (m_datatype) { // to nie jest zbyt optymalne, można by zrobić osobne funkcje case 'f': - fDesiredValue = (*fData) * fScale + fOffset; + m_targetvalue = (*fData); break; case 'd': - fDesiredValue = (*dData) * fScale + fOffset; + m_targetvalue = (*dData); break; case 'i': - fDesiredValue = (*iData) * fScale + fOffset; + m_targetvalue = (*iData); break; } }; +float TGauge::GetScaledValue() const { + + return ( + ( false == m_interpolatescale ) ? + m_value * m_scale + m_offset : + m_value + * interpolate( + m_scale, m_endscale, + clamp( + m_value / m_endvalue, + 0.f, 1.f ) ) + + m_offset ); +} + // returns offset of submodel associated with the button from the model centre glm::vec3 TGauge::model_offset() const { diff --git a/Gauge.h b/Gauge.h index adb105e6..40f82cca 100644 --- a/Gauge.h +++ b/Gauge.h @@ -29,16 +29,13 @@ public: TGauge() = default; inline void Clear() { *this = TGauge(); } - void Init(TSubModel *NewSubModel, TGaugeType eNewTyp, double fNewScale = 1, double fNewOffset = 0, double fNewFriction = 0, double fNewValue = 0); + void Init(TSubModel *Submodel, TGaugeType Type, float Scale = 1, float Offset = 0, float Friction = 0, float Value = 0, float const Endvalue = -1.0, float const Endscale = -1.0, bool const Interpolate = false ); bool Load(cParser &Parser, TDynamicObject const *Owner, TModel3d *md1, TModel3d *md2 = nullptr, double mul = 1.0); - void PermIncValue(double fNewDesired); - void IncValue(double fNewDesired); - void DecValue(double fNewDesired); - void UpdateValue( double fNewDesired ); - void UpdateValue( double fNewDesired, sound_source &Fallbacksound ); - void PutValue(double fNewDesired); - double GetValue() const; - double GetDesiredValue() const; + void UpdateValue( float fNewDesired ); + void UpdateValue( float fNewDesired, sound_source &Fallbacksound ); + void PutValue(float fNewDesired); + float GetValue() const; + float GetDesiredValue() const; void Update(); void AssignFloat(float *fValue); void AssignDouble(double *dValue); @@ -54,15 +51,22 @@ private: // imports member data pair from the config file bool Load_mapping( cParser &Input ); - void UpdateValue( double fNewDesired, sound_source *Fallbacksound ); + void + UpdateValue( float fNewDesired, sound_source *Fallbacksound ); + float + GetScaledValue() const; + // members - TGaugeType eType { gt_Unknown }; // typ ruchu - double fFriction { 0.0 }; // hamowanie przy zliżaniu się do zadanej wartości - double fDesiredValue { 0.0 }; // wartość docelowa - double fValue { 0.0 }; // wartość obecna - double fOffset { 0.0 }; // wartość początkowa ("0") - double fScale { 1.0 }; // wartość końcowa ("1") - char cDataType; // typ zmiennej parametru: f-float, d-double, i-int + TGaugeType m_type { gt_Unknown }; // typ ruchu + float m_friction { 0.f }; // hamowanie przy zliżaniu się do zadanej wartości + float m_targetvalue { 0.f }; // wartość docelowa + float m_value { 0.f }; // wartość obecna + float m_offset { 0.f }; // wartość początkowa ("0") + float m_scale { 1.f }; // scale applied to the value at the start of accepted value range + float m_endscale { -1.f }; // scale applied to the value at the end of accepted value range + float m_endvalue { -1.f }; // end value of accepted value range + bool m_interpolatescale { false }; + char m_datatype; // typ zmiennej parametru: f-float, d-double, i-int union { // wskaźnik na parametr pokazywany przez animację float *fData; diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 3fbbf0c3..e65ae1f2 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -1572,7 +1572,7 @@ void TMoverParameters::WaterHeaterCheck( double const Timestep ) { && ( true == Battery ) && ( true == WaterHeater.is_enabled ) && ( true == WaterHeater.breaker ) - && ( ( WaterHeater.config.temp_min < 0 ) || ( dizel_heat.temperatura1 < WaterHeater.config.temp_min ) ) ); + && ( ( WaterHeater.is_active ) || ( WaterHeater.config.temp_min < 0 ) || ( dizel_heat.temperatura1 < WaterHeater.config.temp_min ) ) ); if( ( WaterHeater.config.temp_max > 0 ) && ( dizel_heat.temperatura1 > WaterHeater.config.temp_max ) ) { @@ -4555,7 +4555,7 @@ double TMoverParameters::TractionForce(double dt) // TBD, TODO: currently ignores RVentType, fix this? RventRot += clamp( enrot - RventRot, -100.0, 50.0 ) * dt; dizel_heat.rpmw += clamp( dizel_heat.rpmwz - dizel_heat.rpmw, -100.f, 50.f ) * dt; - dizel_heat.rpmw += clamp( dizel_heat.rpmwz - dizel_heat.rpmw, -100.f, 50.f ) * dt; + dizel_heat.rpmw2 += clamp( dizel_heat.rpmwz2 - dizel_heat.rpmw2, -100.f, 50.f ) * dt; } else { RventRot *= std::max( 0.0, 1.0 - RVentSpeed * dt ); @@ -4569,7 +4569,7 @@ double TMoverParameters::TractionForce(double dt) // NOTE: we update only radiator fans, as vehicles with diesel engine don't have other ventilators if( true == Mains ) { dizel_heat.rpmw += clamp( dizel_heat.rpmwz - dizel_heat.rpmw, -100.f, 50.f ) * dt; - dizel_heat.rpmw += clamp( dizel_heat.rpmwz - dizel_heat.rpmw, -100.f, 50.f ) * dt; + dizel_heat.rpmw2 += clamp( dizel_heat.rpmwz2 - dizel_heat.rpmw2, -100.f, 50.f ) * dt; } else { dizel_heat.rpmw *= std::max( 0.0, 1.0 - dizel_heat.rpmw * dt ); @@ -4703,11 +4703,18 @@ double TMoverParameters::TractionForce(double dt) // tmpV:=V*CabNo*ActiveDir; auto const tmpV { nrot * Pirazy2 * 0.5 * WheelDiameter * DirAbsolute }; //*CabNo*ActiveDir; // jazda manewrowa - if (ShuntMode) - { - Voltage = (SST[MainCtrlPos].Umax * AnPos) + (SST[MainCtrlPos].Umin * (1.0 - AnPos)); - tmp = (SST[MainCtrlPos].Pmax * AnPos) + (SST[MainCtrlPos].Pmin * (1.0 - AnPos)); - Ft = tmp * 1000.0 / (abs(tmpV) + 1.6); + if( true == ShuntMode ) { + if( ( true == Mains ) && ( MainCtrlPos > 0 ) ) { + Voltage = ( SST[ MainCtrlPos ].Umax * AnPos ) + ( SST[ MainCtrlPos ].Umin * ( 1.0 - AnPos ) ); + // NOTE: very crude way to approximate power generated at current rpm instead of instant top output + auto const rpmratio { 60.0 * enrot / DElist[ MainCtrlPos ].RPM }; + tmp = rpmratio * ( SST[ MainCtrlPos ].Pmax * AnPos ) + ( SST[ MainCtrlPos ].Pmin * ( 1.0 - AnPos ) ); + Ft = tmp * 1000.0 / ( abs( tmpV ) + 1.6 ); + } + else { + Voltage = 0; + Ft = 0; + } PosRatio = 1; } else // jazda ciapongowa diff --git a/Train.cpp b/Train.cpp index 9a991709..2855f13c 100644 --- a/Train.cpp +++ b/Train.cpp @@ -757,7 +757,7 @@ void TTrain::OnCommand_secondcontrollerincrease( TTrain *Train, command_data con // on press or hold if( Train->mvControlled->ShuntMode ) { Train->mvControlled->AnPos = clamp( - Train->mvControlled->AnPos + ( Command.time_delta * 1.0f ), + Train->mvControlled->AnPos + ( Command.time_delta * 2.0 ), 0.0, 1.0 ); } else { @@ -818,7 +818,7 @@ void TTrain::OnCommand_secondcontrollerdecrease( TTrain *Train, command_data con // on press or hold if( Train->mvControlled->ShuntMode ) { Train->mvControlled->AnPos = clamp( - Train->mvControlled->AnPos - ( Command.time_delta * 1.0f ), + Train->mvControlled->AnPos - ( Command.time_delta * 2.0 ), 0.0, 1.0 ); } Train->mvControlled->DecScndCtrl( 1 ); @@ -4867,6 +4867,7 @@ bool TTrain::Update( double const Deltatime ) btLampkaNadmPrzetwB.Turn( tmp->MoverParameters->ConvOvldFlag ); // nadmiarowy przetwornicy? btLampkaPrzetwB.Turn( tmp->MoverParameters->ConverterFlag ); // zalaczenie przetwornicy btLampkaPrzetwBOff.Turn( false == tmp->MoverParameters->ConverterFlag ); + btLampkaMalfunctionB.Turn( tmp->MoverParameters->dizel_heat.PA ); } else // wylaczone { @@ -4881,6 +4882,7 @@ bool TTrain::Update( double const Deltatime ) btLampkaNadmPrzetwB.Turn( false ); btLampkaPrzetwB.Turn( false ); btLampkaPrzetwBOff.Turn( false ); + btLampkaMalfunctionB.Turn( false ); } } @@ -6306,6 +6308,7 @@ void TTrain::clear_cab_controls() btCabLight.Clear(); // hunter-171012 // others btLampkaMalfunction.Clear(); + btLampkaMalfunctionB.Clear(); btLampkaMotorBlowers.Clear(); ggLeftLightButton.Clear(); @@ -6615,6 +6618,7 @@ bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int co { "i-contactorsb:", btLampkaStycznB }, { "i-conv_ovldb:", btLampkaNadmPrzetwB }, { "i-malfunction:", btLampkaMalfunction }, + { "i-malfunctionb:", btLampkaMalfunctionB }, { "i-forward:", btLampkaForward }, { "i-backward:", btLampkaBackward }, { "i-upperlight:", btLampkaUpperLight }, @@ -6937,9 +6941,9 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con if (Parser.getToken() == "analog") { // McZapkie-300302: zegarek - ggClockSInd.Init(DynamicObject->mdKabina->GetFromName("ClockShand"), gt_Rotate, 1.0/60.0, 0, 0); - ggClockMInd.Init(DynamicObject->mdKabina->GetFromName("ClockMhand"), gt_Rotate, 1.0/60.0, 0, 0); - ggClockHInd.Init(DynamicObject->mdKabina->GetFromName("ClockHhand"), gt_Rotate, 1.0/12.0, 0, 0); + ggClockSInd.Init(DynamicObject->mdKabina->GetFromName("ClockShand"), gt_Rotate, 1.0/60.0); + ggClockMInd.Init(DynamicObject->mdKabina->GetFromName("ClockMhand"), gt_Rotate, 1.0/60.0); + ggClockHInd.Init(DynamicObject->mdKabina->GetFromName("ClockHhand"), gt_Rotate, 1.0/12.0); } } else if (Label == "evoltage:") @@ -7002,6 +7006,12 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); gauge.AssignDouble(&mvControlled->DistCounter); } + else if( Label == "shuntmodepower:" ) { + // shunt mode power slider + auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka + gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); + gauge.AssignDouble(&mvControlled->AnPos); + } else { // failed to match the label diff --git a/Train.h b/Train.h index 60636020..46b51400 100644 --- a/Train.h +++ b/Train.h @@ -512,6 +512,7 @@ public: // reszta może by?publiczna TButton btLampkaRearRightEndLight; // other TButton btLampkaMalfunction; + TButton btLampkaMalfunctionB; TButton btLampkaMotorBlowers; TButton btCabLight; // hunter-171012: lampa oswietlajaca kabine From fc1f57a5d40be668c8bc99a06b314a9da981d223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=B3lik=20Uszasty?= Date: Tue, 17 Apr 2018 22:42:26 +0200 Subject: [PATCH 5/6] Fixed BrakeHandle type MHZ_EN57 and brake operation mode selector --- DynObj.cpp | 2 +- Train.cpp | 36 ++++++++++++++++++++++++++++++++++++ Train.h | 2 ++ command.cpp | 2 ++ command.h | 2 ++ keyboardinput.cpp | 4 ++++ 6 files changed, 47 insertions(+), 1 deletion(-) diff --git a/DynObj.cpp b/DynObj.cpp index 81b465a5..143d4cf0 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -3025,7 +3025,7 @@ bool TDynamicObject::Update(double dt, double dt1) } auto FzadED { 0.0 }; - if( ( MoverParameters->EpFuse ) + if( ( MoverParameters->EpFuse && (MoverParameters->BrakeHandle != MHZ_EN57)) || ( ( MoverParameters->BrakeHandle == MHZ_EN57 ) && ( MoverParameters->BrakeOpModeFlag & bom_MED ) ) ) { FzadED = std::min( Fzad, FmaxED ); diff --git a/Train.cpp b/Train.cpp index 2855f13c..1000932f 100644 --- a/Train.cpp +++ b/Train.cpp @@ -193,6 +193,8 @@ TTrain::commandhandler_map const TTrain::m_commandhandlers = { { user_command::wheelspinbrakeactivate, &TTrain::OnCommand_wheelspinbrakeactivate }, { user_command::sandboxactivate, &TTrain::OnCommand_sandboxactivate }, { user_command::epbrakecontroltoggle, &TTrain::OnCommand_epbrakecontroltoggle }, + { user_command::brakeoperationmodeincrease, &TTrain::OnCommand_brakeoperationmodeincrease }, + { user_command::brakeoperationmodedecrease, &TTrain::OnCommand_brakeoperationmodedecrease }, { user_command::brakeactingspeedincrease, &TTrain::OnCommand_brakeactingspeedincrease }, { user_command::brakeactingspeeddecrease, &TTrain::OnCommand_brakeactingspeeddecrease }, { user_command::brakeactingspeedsetcargo, &TTrain::OnCommand_brakeactingspeedsetcargo }, @@ -1264,6 +1266,40 @@ void TTrain::OnCommand_epbrakecontroltoggle( TTrain *Train, command_data const & } } +void TTrain::OnCommand_brakeoperationmodeincrease(TTrain *Train, command_data const &Command) { + + if (Command.action == GLFW_PRESS) { + // only reacting to press, so the switch doesn't flip back and forth if key is held down + if (0 < (Train->mvOccupied->BrakeOpModeFlag * 2) & Train->mvOccupied->BrakeOpModes) { + // next mode + Train->mvOccupied->BrakeOpModeFlag *= 2; + // audio feedback + Train->dsbPneumaticSwitch.play(); + // visual feedback + // NOTE: there's no button for brake operation mode switch + // TBD, TODO: add brake operation mode switch? + } + } +} + +void TTrain::OnCommand_brakeoperationmodedecrease(TTrain *Train, command_data const &Command) { + + if (Command.action == GLFW_PRESS) { + // only reacting to press, so the switch doesn't flip back and forth if key is held down + if (0 < (Train->mvOccupied->BrakeOpModeFlag / 2) & Train->mvOccupied->BrakeOpModes) { + // previous mode + Train->mvOccupied->BrakeOpModeFlag /= 2; + // audio feedback + Train->dsbPneumaticSwitch.play(); + // visual feedback + // NOTE: there's no button for brake operation mode switch + // TBD, TODO: add brake operation mode switch? + } + } +} + + + void TTrain::OnCommand_brakeactingspeedincrease( TTrain *Train, command_data const &Command ) { if( Command.action == GLFW_PRESS ) { diff --git a/Train.h b/Train.h index 46b51400..020bda93 100644 --- a/Train.h +++ b/Train.h @@ -184,6 +184,8 @@ class TTrain static void OnCommand_wheelspinbrakeactivate( TTrain *Train, command_data const &Command ); static void OnCommand_sandboxactivate( TTrain *Train, command_data const &Command ); static void OnCommand_epbrakecontroltoggle( TTrain *Train, command_data const &Command ); + static void OnCommand_brakeoperationmodeincrease(TTrain *Train, command_data const &Command); + static void OnCommand_brakeoperationmodedecrease(TTrain *Train, command_data const &Command); static void OnCommand_brakeactingspeedincrease( TTrain *Train, command_data const &Command ); static void OnCommand_brakeactingspeeddecrease( TTrain *Train, command_data const &Command ); static void OnCommand_brakeactingspeedsetcargo( TTrain *Train, command_data const &Command ); diff --git a/command.cpp b/command.cpp index c7b0073f..8fb41b2c 100644 --- a/command.cpp +++ b/command.cpp @@ -102,6 +102,8 @@ commanddescription_sequence Commands_descriptions = { { "motoroverloadrelayreset", command_target::vehicle }, { "notchingrelaytoggle", command_target::vehicle }, { "epbrakecontroltoggle", command_target::vehicle }, + { "brakeoperationmodeincrease", command_target::vehicle }, + { "brakeoperationmodedecrease", command_target::vehicle }, { "brakeactingspeedincrease", command_target::vehicle }, { "brakeactingspeeddecrease", command_target::vehicle }, { "brakeactingspeedsetcargo", command_target::vehicle }, diff --git a/command.h b/command.h index be90b1c8..db47786f 100644 --- a/command.h +++ b/command.h @@ -96,6 +96,8 @@ enum class user_command { motoroverloadrelayreset, notchingrelaytoggle, epbrakecontroltoggle, + brakeoperationmodeincrease, + brakeoperationmodedecrease, brakeactingspeedincrease, brakeactingspeeddecrease, brakeactingspeedsetcargo, diff --git a/keyboardinput.cpp b/keyboardinput.cpp index 4f60b08f..67e5e850 100644 --- a/keyboardinput.cpp +++ b/keyboardinput.cpp @@ -333,6 +333,10 @@ keyboard_input::default_bindings() { { GLFW_KEY_G }, // epbrakecontroltoggle { GLFW_KEY_Z | keymodifier::control }, + // brakeoperationmodeincrease + { GLFW_KEY_L | keymodifier::shift }, + // brakeoperationmodedecrease + { GLFW_KEY_L}, // brakeactingspeedincrease { GLFW_KEY_B | keymodifier::shift }, // brakeactingspeeddecrease From 430ab99f8e20bfd6fb0e8db6a0b0b03beda07f44 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Wed, 18 Apr 2018 22:14:16 +0200 Subject: [PATCH 6/6] compressor start method, minor ai vehicle preparation logic tweaks, minor refactoring --- Driver.cpp | 12 +++--- McZapkie/MOVER.h | 5 ++- McZapkie/Mover.cpp | 97 ++++++++++++++++++++++++++-------------------- Train.cpp | 22 +++++------ Train.h | 4 +- command.cpp | 4 +- command.h | 4 +- keyboardinput.cpp | 8 ++-- version.h | 2 +- 9 files changed, 86 insertions(+), 72 deletions(-) diff --git a/Driver.cpp b/Driver.cpp index 6af63776..c7c5793f 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -2237,8 +2237,8 @@ bool TController::PrepareEngine() } if (mvControlling->EnginePowerSource.SourceType == CurrentCollector) { // jeśli silnikowy jest pantografującym - mvControlling->PantFront( true ); - mvControlling->PantRear( true ); + mvOccupied->PantFront( true ); + mvOccupied->PantRear( true ); if (mvControlling->PantPress < 4.2) { // załączenie małej sprężarki if( mvControlling->TrainType != dt_EZT ) { @@ -2266,11 +2266,11 @@ bool TController::PrepareEngine() if( !iDirection ) { // jeśli nie ma ustalonego kierunku if( ( mvControlling->PantFrontVolt != 0.0 ) || ( mvControlling->PantRearVolt != 0.0 ) || voltfront || voltrear ) { - if( mvOccupied->Couplers[ 1 ].CouplingFlag == ctrain_virtual ) { + if( mvOccupied->Couplers[ 1 ].CouplingFlag == coupling::faux ) { // jeśli z tyłu nie ma nic iDirection = -1; // jazda w kierunku sprzęgu 1 } - if( mvOccupied->Couplers[ 0 ].CouplingFlag == ctrain_virtual ) { + if( mvOccupied->Couplers[ 0 ].CouplingFlag == coupling::faux ) { // jeśli z przodu nie ma nic iDirection = 1; // jazda w kierunku sprzęgu 0 } @@ -2332,7 +2332,7 @@ bool TController::PrepareEngine() OK = ( OrderDirectionChange( iDirection, mvOccupied ) == -1 ); mvOccupied->ConverterSwitch( true ); // w EN57 sprężarka w ra jest zasilana z silnikowego - mvControlling->CompressorSwitch( true ); + mvOccupied->CompressorSwitch( true ); } } else @@ -2341,7 +2341,7 @@ bool TController::PrepareEngine() else OK = false; - OK = OK && (mvOccupied->ActiveDir != 0) && (mvControlling->CompressorAllow) && (workingtemperature); + OK = OK && ( mvOccupied->ActiveDir != 0 ) && ( mvControlling->ScndPipePress > 4.5 ) && ( workingtemperature ); if (OK) { if (eStopReason == stopSleep) // jeśli dotychczas spał diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index a67278b5..4a1cd438 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -860,7 +860,7 @@ public: double RVentCutOff = 0.0; /*rezystancja wylaczania wentylatorow dla RVentType=2*/ double RVentSpeed { 0.5 }; //rozpedzanie sie wentylatora obr/s^2} double RVentMinI { 50.0 }; //przy jakim pradzie sie wylaczaja} - int CompressorPower = 1; /*0: bezp. z obwodow silnika, 1: z przetwornicy, reczne, 2: w przetwornicy, stale, 5: z silnikowego*/ + int CompressorPower = 1; // 0: main circuit, 1: z przetwornicy, reczne, 2: w przetwornicy, stale, 3: diesel engine, 4: converter of unit in front, 5: converter of unit behind int SmallCompressorPower = 0; /*Winger ZROBIC*/ bool Trafo = false; /*pojazd wyposażony w transformator*/ @@ -993,6 +993,7 @@ public: bool CompressorAllow = false; /*! zezwolenie na uruchomienie sprezarki NBMX*/ bool CompressorAllowLocal{ true }; // local device state override (most units don't have this fitted so it's set to true not to intefere) bool CompressorGovernorLock{ false }; // indicates whether compressor pressure switch was activated due to reaching cut-out pressure + start CompressorStart{ start::manual }; // whether the compressor is started manually, or another way // TODO converter parameters, for when we start cleaning up mover parameters start ConverterStart{ start::manual }; // whether converter is started manually, or by other means float ConverterStartDelay{ 0.0f }; // delay (in seconds) before the converter is started, once its activation conditions are met @@ -1269,7 +1270,7 @@ public: bool IncBrakePress(double &brake, double PressLimit, double dp); bool DecBrakePress(double &brake, double PressLimit, double dp); bool BrakeDelaySwitch(int BDS);/*! przelaczanie nastawy opoznienia*/ - bool IncBrakeMult(void);/*przelaczanie prozny/ladowny*/ + bool IncBrakeMult(void);/*przelaczanie prozny/ladowny*/ bool DecBrakeMult(void); /*pomocnicze funkcje dla ukladow pneumatycznych*/ void UpdateBrakePressure(double dt); diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index e65ae1f2..d16bac76 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -2679,7 +2679,7 @@ bool TMoverParameters::ConverterSwitch( bool State, int const Notify ) // ************************************************************************************************* bool TMoverParameters::CompressorSwitch( bool State, int const Notify ) { - if( CompressorPower > 1 ) { + if( CompressorStart != start::manual ) { // only pay attention if the compressor can be controlled manually return false; } @@ -3323,35 +3323,38 @@ void TMoverParameters::CompressorCheck(double dt) else { if( CompressorPower == 3 ) { // experimental: make sure compressor coupled with diesel engine is always ready for work - CompressorAllow = true; + CompressorStart = start::automatic; } if (CompressorFlag) // jeśli sprężarka załączona { // sprawdzić możliwe warunki wyłączenia sprężarki if (CompressorPower == 5) // jeśli zasilanie z sąsiedniego członu { // zasilanie sprężarki w członie ra z członu silnikowego (sprzęg 1) - if (Couplers[1].Connected != NULL) - CompressorFlag = - ( Couplers[ 1 ].Connected->CompressorAllow - && Couplers[ 1 ].Connected->CompressorAllowLocal - && Couplers[ 1 ].Connected->Mains - && Couplers[ 1 ].Connected->ConverterFlag ); - else - CompressorFlag = false; // bez tamtego członu nie zadziała + if( Couplers[ side::rear ].Connected != NULL ) { + CompressorFlag = ( + ( ( CompressorAllow ) || ( CompressorStart == start::automatic ) ) + && ( CompressorAllowLocal ) + && ( Couplers[ side::rear ].Connected->ConverterFlag ) ); + } + else { + // bez tamtego członu nie zadziała + CompressorFlag = false; + } } else if (CompressorPower == 4) // jeśli zasilanie z poprzedniego członu { // zasilanie sprężarki w członie ra z członu silnikowego (sprzęg 1) - if (Couplers[0].Connected != NULL) - CompressorFlag = - ( Couplers[ 0 ].Connected->CompressorAllow - && Couplers[ 0 ].Connected->CompressorAllowLocal - && Couplers[ 0 ].Connected->Mains - && Couplers[ 0 ].Connected->ConverterFlag ); - else + if( Couplers[ side::front ].Connected != NULL ) { + CompressorFlag = ( + ( ( CompressorAllow ) || ( CompressorStart == start::automatic ) ) + && ( CompressorAllowLocal ) + && ( Couplers[ side::front ].Connected->ConverterFlag ) ); + } + else { CompressorFlag = false; // bez tamtego członu nie zadziała + } } else - CompressorFlag = - ( ( CompressorAllow ) + CompressorFlag = ( + ( ( CompressorAllow ) || ( CompressorStart == start::automatic ) ) && ( CompressorAllowLocal ) && ( Mains ) && ( ( ConverterFlag ) @@ -3402,38 +3405,37 @@ void TMoverParameters::CompressorCheck(double dt) // or if the switch is on and the pressure isn't maxed if( CompressorPower == 5 ) // jeśli zasilanie z następnego członu { // zasilanie sprężarki w członie ra z członu silnikowego (sprzęg 1) - if( Couplers[ 1 ].Connected != nullptr ) { - CompressorFlag = - ( Couplers[ 1 ].Connected->CompressorAllow - && Couplers[ 1 ].Connected->CompressorAllowLocal - && Couplers[ 1 ].Connected->Mains - && Couplers[ 1 ].Connected->ConverterFlag ); - } + if( Couplers[ side::rear ].Connected != NULL ) { + CompressorFlag = ( + ( ( CompressorAllow ) || ( CompressorStart == start::automatic ) ) + && ( CompressorAllowLocal ) + && ( Couplers[ side::rear ].Connected->ConverterFlag ) ); + } else { - CompressorFlag = false; // bez tamtego członu nie zadziała + // bez tamtego członu nie zadziała + CompressorFlag = false; } } else if( CompressorPower == 4 ) // jeśli zasilanie z poprzedniego członu { // zasilanie sprężarki w członie ra z członu silnikowego (sprzęg 1) - if( Couplers[ 0 ].Connected != nullptr ) { - CompressorFlag = - ( Couplers[ 0 ].Connected->CompressorAllow - && Couplers[ 0 ].Connected->CompressorAllowLocal - && Couplers[ 0 ].Connected->Mains - && Couplers[ 0 ].Connected->ConverterFlag ); + if( Couplers[ side::front ].Connected != NULL ) { + CompressorFlag = ( + ( ( CompressorAllow ) || ( CompressorStart == start::automatic ) ) + && ( CompressorAllowLocal ) + && ( Couplers[ side::front ].Connected->ConverterFlag ) ); } else { CompressorFlag = false; // bez tamtego członu nie zadziała } } else { - CompressorFlag = - ( ( CompressorAllow ) - && ( CompressorAllowLocal ) - && ( Mains ) - && ( ( ConverterFlag ) - || ( CompressorPower == 0 ) - || ( CompressorPower == 3 ) ) ); + CompressorFlag = ( + ( ( CompressorAllow ) || ( CompressorStart == start::automatic ) ) + && ( CompressorAllowLocal ) + && ( Mains ) + && ( ( ConverterFlag ) + || ( CompressorPower == 0 ) + || ( CompressorPower == 3 ) ) ); } // NOTE: crude way to enforce simultaneous activation of compressors in multi-unit setups @@ -8143,6 +8145,19 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) { } extract_value( ConverterStartDelay, "ConverterStartDelay", line, "" ); + // compressor + { + std::map starts { + { "Manual", start::manual }, + { "Automatic", start::automatic } + }; + auto lookup = starts.find( extract_value( "CompressorStart", line ) ); + CompressorStart = + lookup != starts.end() ? + lookup->second : + start::manual; + } + // fuel pump { std::map starts { @@ -9256,7 +9271,7 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C // end else if (Command == "CompressorSwitch") /*NBMX*/ { - if( CompressorPower < 2 ) { + if( CompressorStart == start::manual ) { CompressorAllow = ( CValue1 == 1 ); } OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); diff --git a/Train.cpp b/Train.cpp index 1000932f..ad0f59b3 100644 --- a/Train.cpp +++ b/Train.cpp @@ -193,8 +193,8 @@ TTrain::commandhandler_map const TTrain::m_commandhandlers = { { user_command::wheelspinbrakeactivate, &TTrain::OnCommand_wheelspinbrakeactivate }, { user_command::sandboxactivate, &TTrain::OnCommand_sandboxactivate }, { user_command::epbrakecontroltoggle, &TTrain::OnCommand_epbrakecontroltoggle }, - { user_command::brakeoperationmodeincrease, &TTrain::OnCommand_brakeoperationmodeincrease }, - { user_command::brakeoperationmodedecrease, &TTrain::OnCommand_brakeoperationmodedecrease }, + { user_command::trainbrakeoperationmodeincrease, &TTrain::OnCommand_trainbrakeoperationmodeincrease }, + { user_command::trainbrakeoperationmodedecrease, &TTrain::OnCommand_trainbrakeoperationmodedecrease }, { user_command::brakeactingspeedincrease, &TTrain::OnCommand_brakeactingspeedincrease }, { user_command::brakeactingspeeddecrease, &TTrain::OnCommand_brakeactingspeeddecrease }, { user_command::brakeactingspeedsetcargo, &TTrain::OnCommand_brakeactingspeedsetcargo }, @@ -1266,14 +1266,14 @@ void TTrain::OnCommand_epbrakecontroltoggle( TTrain *Train, command_data const & } } -void TTrain::OnCommand_brakeoperationmodeincrease(TTrain *Train, command_data const &Command) { +void TTrain::OnCommand_trainbrakeoperationmodeincrease(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down - if (0 < (Train->mvOccupied->BrakeOpModeFlag * 2) & Train->mvOccupied->BrakeOpModes) { - // next mode - Train->mvOccupied->BrakeOpModeFlag *= 2; - // audio feedback + if( ( ( Train->mvOccupied->BrakeOpModeFlag << 1 ) & Train->mvOccupied->BrakeOpModes ) != 0 ) { + // next mode + Train->mvOccupied->BrakeOpModeFlag <<= 1; + // audio feedback Train->dsbPneumaticSwitch.play(); // visual feedback // NOTE: there's no button for brake operation mode switch @@ -1282,13 +1282,13 @@ void TTrain::OnCommand_brakeoperationmodeincrease(TTrain *Train, command_data co } } -void TTrain::OnCommand_brakeoperationmodedecrease(TTrain *Train, command_data const &Command) { +void TTrain::OnCommand_trainbrakeoperationmodedecrease(TTrain *Train, command_data const &Command) { if (Command.action == GLFW_PRESS) { // only reacting to press, so the switch doesn't flip back and forth if key is held down - if (0 < (Train->mvOccupied->BrakeOpModeFlag / 2) & Train->mvOccupied->BrakeOpModes) { + if( ( ( Train->mvOccupied->BrakeOpModeFlag >> 1 ) & Train->mvOccupied->BrakeOpModes ) != 0 ) { // previous mode - Train->mvOccupied->BrakeOpModeFlag /= 2; + Train->mvOccupied->BrakeOpModeFlag >>= 1; // audio feedback Train->dsbPneumaticSwitch.play(); // visual feedback @@ -1298,8 +1298,6 @@ void TTrain::OnCommand_brakeoperationmodedecrease(TTrain *Train, command_data co } } - - void TTrain::OnCommand_brakeactingspeedincrease( TTrain *Train, command_data const &Command ) { if( Command.action == GLFW_PRESS ) { diff --git a/Train.h b/Train.h index 020bda93..5880dc47 100644 --- a/Train.h +++ b/Train.h @@ -184,8 +184,8 @@ class TTrain static void OnCommand_wheelspinbrakeactivate( TTrain *Train, command_data const &Command ); static void OnCommand_sandboxactivate( TTrain *Train, command_data const &Command ); static void OnCommand_epbrakecontroltoggle( TTrain *Train, command_data const &Command ); - static void OnCommand_brakeoperationmodeincrease(TTrain *Train, command_data const &Command); - static void OnCommand_brakeoperationmodedecrease(TTrain *Train, command_data const &Command); + static void OnCommand_trainbrakeoperationmodeincrease(TTrain *Train, command_data const &Command); + static void OnCommand_trainbrakeoperationmodedecrease(TTrain *Train, command_data const &Command); static void OnCommand_brakeactingspeedincrease( TTrain *Train, command_data const &Command ); static void OnCommand_brakeactingspeeddecrease( TTrain *Train, command_data const &Command ); static void OnCommand_brakeactingspeedsetcargo( TTrain *Train, command_data const &Command ); diff --git a/command.cpp b/command.cpp index 8fb41b2c..657e99b8 100644 --- a/command.cpp +++ b/command.cpp @@ -102,8 +102,8 @@ commanddescription_sequence Commands_descriptions = { { "motoroverloadrelayreset", command_target::vehicle }, { "notchingrelaytoggle", command_target::vehicle }, { "epbrakecontroltoggle", command_target::vehicle }, - { "brakeoperationmodeincrease", command_target::vehicle }, - { "brakeoperationmodedecrease", command_target::vehicle }, + { "trainbrakeoperationmodeincrease", command_target::vehicle }, + { "trainbrakeoperationmodedecrease", command_target::vehicle }, { "brakeactingspeedincrease", command_target::vehicle }, { "brakeactingspeeddecrease", command_target::vehicle }, { "brakeactingspeedsetcargo", command_target::vehicle }, diff --git a/command.h b/command.h index db47786f..96ecefd5 100644 --- a/command.h +++ b/command.h @@ -96,8 +96,8 @@ enum class user_command { motoroverloadrelayreset, notchingrelaytoggle, epbrakecontroltoggle, - brakeoperationmodeincrease, - brakeoperationmodedecrease, + trainbrakeoperationmodeincrease, + trainbrakeoperationmodedecrease, brakeactingspeedincrease, brakeactingspeeddecrease, brakeactingspeedsetcargo, diff --git a/keyboardinput.cpp b/keyboardinput.cpp index 67e5e850..76d5da28 100644 --- a/keyboardinput.cpp +++ b/keyboardinput.cpp @@ -333,10 +333,10 @@ keyboard_input::default_bindings() { { GLFW_KEY_G }, // epbrakecontroltoggle { GLFW_KEY_Z | keymodifier::control }, - // brakeoperationmodeincrease - { GLFW_KEY_L | keymodifier::shift }, - // brakeoperationmodedecrease - { GLFW_KEY_L}, + // trainbrakeoperationmodeincrease + { GLFW_KEY_KP_3 | keymodifier::shift | keymodifier::control }, + // trainbrakeoperationmodedecrease + { GLFW_KEY_KP_9 | keymodifier::shift | keymodifier::control }, // brakeactingspeedincrease { GLFW_KEY_B | keymodifier::shift }, // brakeactingspeeddecrease diff --git a/version.h b/version.h index f1e336ed..85730745 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 18 -#define VERSION_MINOR 411 +#define VERSION_MINOR 418 #define VERSION_REVISION 0