diff --git a/Driver.cpp b/Driver.cpp index a4666bea..ddba2e45 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -2440,6 +2440,11 @@ bool TController::PrepareEngine() mvOccupied->ConverterSwitch( true ); // w EN57 sprężarka w ra jest zasilana z silnikowego mvOccupied->CompressorSwitch( true ); + // enable motor blowers + mvOccupied->MotorBlowersSwitchOff( false, side::front ); + mvOccupied->MotorBlowersSwitch( true, side::front ); + mvOccupied->MotorBlowersSwitchOff( false, side::rear ); + mvOccupied->MotorBlowersSwitch( true, side::rear ); } } else diff --git a/DynObj.cpp b/DynObj.cpp index 360ff418..6f92ca1e 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -5257,6 +5257,35 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co } } + else if( token == "motorblower:" ) { + + sound_source blowertemplate { sound_placement::engine }; + blowertemplate.deserialize( parser, sound_type::single, sound_parameters::range | sound_parameters::amplitude | sound_parameters::frequency ); + blowertemplate.owner( this ); + + auto const amplitudedivisor = static_cast( + MoverParameters->MotorBlowers[ side::front ].speed > 0.f ? + MoverParameters->MotorBlowers[ side::front ].speed * MoverParameters->nmax * 60 + MoverParameters->Power * 3 : + MoverParameters->MotorBlowers[ side::front ].speed * -1 ); + blowertemplate.m_amplitudefactor /= amplitudedivisor; + blowertemplate.m_frequencyfactor /= amplitudedivisor; + + if( true == m_powertrainsounds.motors.empty() ) { + // fallback for cases without specified motor locations, convert sound template to a single sound source + m_powertrainsounds.motorblowers.emplace_back( blowertemplate ); + } + else { + // apply configuration to all defined motor blowers + for( auto &blower : m_powertrainsounds.motorblowers ) { + // combine potential x- and y-axis offsets of the sound template with z-axis offsets of individual blowers + auto bloweroffset { blowertemplate.offset() }; + bloweroffset.z = blower.offset().z; + blower = blowertemplate; + blower.offset( bloweroffset ); + } + } + } + else if( token == "inverter:" ) { // plik z dzwiekiem wentylatora, mnozniki i ofsety amp. i czest. m_powertrainsounds.inverter.deserialize( parser, sound_type::single, sound_parameters::range | sound_parameters::amplitude | sound_parameters::frequency ); @@ -5604,10 +5633,13 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co auto const offset { std::atof( token.c_str() ) * -1.f }; // NOTE: we skip setting owner of the sounds, it'll be done during individual sound deserialization sound_source motor { sound_placement::external }; // generally traction motor + sound_source motorblower { sound_placement::engine }; // associated motor blowers // add entry to the list auto const location { glm::vec3 { 0.f, 0.f, offset } }; motor.offset( location ); m_powertrainsounds.motors.emplace_back( motor ); + motorblower.offset( location ); + m_powertrainsounds.motorblowers.emplace_back( motorblower ); } } @@ -6710,6 +6742,27 @@ TDynamicObject::powertrain_sounds::render( TMoverParameters const &Vehicle, doub motor.stop(); } } + // motor blowers + if( false == motorblowers.empty() ) { + for( auto &blowersound : motorblowers ) { + // match the motor blower and the sound source based on whether they're located in the front or the back of the vehicle + auto const &blower { Vehicle.MotorBlowers[ ( blowersound.offset().z > 0 ? side::front : side::rear ) ] }; + if( blower.revolutions > 1 ) { + + blowersound + .pitch( + true == blowersound.is_combined() ? + blower.revolutions * 0.01f : + blowersound.m_frequencyoffset + blowersound.m_frequencyfactor * blower.revolutions ) + .gain( blowersound.m_amplitudeoffset + blowersound.m_amplitudefactor * blower.revolutions ) + .play( sound_flags::exclusive | sound_flags::looping ); + } + else { + blowersound.stop(); + } + } + } + // inverter sounds if( Vehicle.EngineType == TEngineType::ElectricInductionMotor ) { if( Vehicle.InverterFrequency > 0.1 ) { diff --git a/DynObj.h b/DynObj.h index 0a19dee1..f0731d3a 100644 --- a/DynObj.h +++ b/DynObj.h @@ -327,6 +327,7 @@ private: struct powertrain_sounds { sound_source inverter { sound_placement::engine }; + std::vector motorblowers; std::vector motors; // generally traction motor(s) double motor_volume { 0.0 }; // MC: pomocnicze zeby gladziej silnik buczal float motor_momentum { 0.f }; // recent change in motor revolutions diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index fed359ee..57d948a1 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -166,6 +166,7 @@ enum class start_t { manual, automatic, manualwithautofallback, + converter, battery }; // recognized vehicle light locations and types; can be combined @@ -619,107 +620,122 @@ struct TCoupling { int sounds { 0 }; // sounds emitted by the coupling devices }; -// basic approximation of a fuel pump -// TODO: fuel consumption, optional automatic engine start after activation -struct fuel_pump { - - bool is_enabled { false }; // device is allowed/requested to operate - bool is_disabled { false }; // device is requested to stop - bool is_active { false }; // device is working - start_t start_type { start_t::manual }; -}; - -// basic approximation of a fuel pump -// TODO: fuel consumption, optional automatic engine start after activation -struct oil_pump { - - bool is_enabled { false }; // device is allowed/requested to operate - bool is_disabled { false }; // device is requested to stop - bool is_active { false }; // device is working - start_t start_type { start_t::manual }; - float resource_amount { 1.f }; - float pressure_minimum { 0.f }; // lowest acceptable working pressure - float pressure_maximum { 0.65f }; // oil pressure at maximum engine revolutions - float pressure_target { 0.f }; - 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_disabled { false }; // device is requested to stop - bool is_active { false }; // device is working - start_t start_type { start_t::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 - double fan_speed { 0.075 }; // cooling fan rpm; either fraction of engine rpm, or absolute value if negative - // 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 :) +private: +// types + + // basic approximation of a generic device + // TBD: inheritance or composition? + struct basic_device { + // config + start_t start_type { start_t::manual }; + // ld inputs + bool is_enabled { false }; // device is allowed/requested to operate + bool is_disabled { false }; // device is requested to stop + // TODO: add remaining inputs; start conditions and potential breakers + // ld outputs + bool is_active { false }; // device is working + }; + + struct cooling_fan : public basic_device { + // config + float speed { 0.f }; // cooling fan rpm; either fraction of parent rpm, or absolute value if negative + // ld outputs + float revolutions { 0.f }; // current fan rpm + }; + + // basic approximation of a fuel pump + struct fuel_pump : public basic_device { + // TODO: fuel consumption, optional automatic engine start after activation + }; + + // basic approximation of an oil pump + struct oil_pump : public basic_device { + // config + float pressure_minimum { 0.f }; // lowest acceptable working pressure + float pressure_maximum { 0.65f }; // oil pressure at maximum engine revolutions + // ld inputs + float resource_amount { 1.f }; // amount of affected resource, compared to nominal value + // internal data + float pressure_target { 0.f }; + // ld outputs + float pressure { 0.f }; // current pressure + }; + + // basic approximation of a water pump + struct water_pump : public basic_device { + // ld inputs + // TODO: move to breaker list in the basic device once implemented + bool breaker { true }; // device is allowed to operate + }; + + struct water_heater { + // config + struct heater_config_t { + float temp_min { -1 }; // lowest accepted temperature + float temp_max { -1 }; // highest accepted temperature + } config; + // ld inputs + bool breaker { true }; // device is allowed to operate + bool is_enabled { false }; // device is requested to operate + // ld outputs + bool is_active { false }; // device is working + bool is_damaged { false }; // device is damaged + }; + + 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 + double fan_speed { 0.075 }; // cooling fan rpm; either fraction of engine rpm, or absolute value if negative + // 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 }; + }; + public: double dMoveLen = 0.0; @@ -1040,6 +1056,7 @@ public: water_heater WaterHeater; bool WaterCircuitsLink { false }; // optional connection between water circuits heat_data dizel_heat; + std::array MotorBlowers; int BrakeCtrlPos = -2; /*nastawa hamulca zespolonego*/ double BrakeCtrlPosR = 0.0; /*nastawa hamulca zespolonego - plynna dla FV4a*/ @@ -1126,7 +1143,7 @@ public: bool StLinSwitchOff{ false }; // state of the button forcing motor connectors open bool ResistorsFlag = false; /*!o jazda rezystorowa*/ double RventRot = 0.0; /*!s obroty wentylatorow rozruchowych*/ - bool UnBrake = false; /*w EZT - nacisniete odhamowywanie*/ + bool UnBrake = false; /*w EZT - nacisniete odhamowywanie*/ double PantPress = 0.0; /*Cisnienie w zbiornikach pantografow*/ bool PantPressSwitchActive{ false }; // state of the pantograph pressure switch. gets primed at defined pressure level in pantograph air system bool PantPressLockActive{ false }; // pwr system state flag. fires when pressure switch activates by pantograph pressure dropping below defined level @@ -1343,6 +1360,8 @@ public: bool FuelPumpSwitchOff( bool State, range_t const Notify = range_t::consist ); // fuel pump state toggle bool OilPumpSwitch( bool State, range_t const Notify = range_t::consist ); // oil pump state toggle bool OilPumpSwitchOff( bool State, range_t const Notify = range_t::consist ); // oil pump state toggle + bool MotorBlowersSwitch( bool State, side const Side, range_t const Notify = range_t::consist ); // traction motor fan state toggle + bool MotorBlowersSwitchOff( bool State, side const Side, range_t const Notify = range_t::consist ); // traction motor fan state toggle bool MainSwitch( bool const State, range_t const Notify = range_t::consist );/*! wylacznik glowny*/ bool ConverterSwitch( bool State, range_t const Notify = range_t::consist );/*! wl/wyl przetwornicy*/ bool CompressorSwitch( bool State, range_t const Notify = range_t::consist );/*! wl/wyl sprezarki*/ @@ -1353,6 +1372,7 @@ public: void WaterHeaterCheck( double const Timestep ); void FuelPumpCheck( double const Timestep ); void OilPumpCheck( double const Timestep ); + void MotorBlowersCheck( double const Timestep ); bool FuseOn(void); //bezpiecznik nadamiary bool FuseFlagCheck(void) const; // sprawdzanie flagi nadmiarowego void FuseOff(void); // wylaczenie nadmiarowego diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 34fd7fd3..02a2a78b 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -1438,6 +1438,8 @@ void TMoverParameters::compute_movement_( double const Deltatime ) { SetFlag( SoundFlag, sound::relay ); } } + // traction motors + MotorBlowersCheck( Deltatime ); // uklady hamulcowe: ConverterCheck( Deltatime ); if (VeselVolume > 0) @@ -1534,6 +1536,11 @@ void TMoverParameters::WaterPumpCheck( double const Timestep ) { // water heater status check void TMoverParameters::WaterHeaterCheck( double const Timestep ) { + WaterHeater.is_damaged = ( + ( true == WaterHeater.is_damaged ) + || ( ( true == WaterHeater.is_active ) + && ( false == WaterPump.is_active ) ) ); + WaterHeater.is_active = ( ( false == WaterHeater.is_damaged ) && ( true == Battery ) @@ -1545,11 +1552,6 @@ void TMoverParameters::WaterHeaterCheck( double const Timestep ) { && ( dizel_heat.temperatura1 > WaterHeater.config.temp_max ) ) { WaterHeater.is_active = false; } - - WaterHeater.is_damaged = ( - ( true == WaterHeater.is_damaged ) - || ( ( true == WaterHeater.is_active ) - && ( false == WaterPump.is_active ) ) ); } // fuel pump status update @@ -1592,20 +1594,57 @@ void TMoverParameters::OilPumpCheck( double const Timestep ) { true == OilPump.is_active ? std::min( minpressure + 0.1f, OilPump.pressure_maximum ) : // slight pressure margin to give time to switch off the pump and start the engine 0.f ); - if( OilPump.pressure_present < OilPump.pressure_target ) { + if( OilPump.pressure < OilPump.pressure_target ) { // TODO: scale change rate from 0.01-0.05 with oil/engine temperature/idle time - OilPump.pressure_present = + OilPump.pressure = std::min( OilPump.pressure_target, - OilPump.pressure_present + ( enrot > 5.0 ? 0.05 : 0.035 ) * Timestep ); + OilPump.pressure + ( enrot > 5.0 ? 0.05 : 0.035 ) * Timestep ); } - if( OilPump.pressure_present > OilPump.pressure_target ) { - OilPump.pressure_present = + if( OilPump.pressure > OilPump.pressure_target ) { + OilPump.pressure = std::max( OilPump.pressure_target, - OilPump.pressure_present - 0.01 * Timestep ); + OilPump.pressure - 0.01 * Timestep ); + } + OilPump.pressure = clamp( OilPump.pressure, 0.f, 1.5f ); +} + +void TMoverParameters::MotorBlowersCheck( double const Timestep ) { + // activation check + for( auto &blower : MotorBlowers ) { + + blower.is_active = ( + // TODO: bind properly power source when ld is in place + ( blower.start_type == start_t::battery ? Battery : + blower.start_type == start_t::converter ? ConverterFlag : + Mains ) // power source + // breaker condition disabled until it's implemented in the class data +// && ( true == blower.breaker ) + && ( false == blower.is_disabled ) + && ( ( true == blower.is_active ) + || ( blower.start_type == start_t::manual ? blower.is_enabled : true ) ) ); + } + // update + for( auto &fan : MotorBlowers ) { + + auto const revolutionstarget { ( + fan.is_active ? + ( fan.speed > 0.f ? fan.speed * static_cast( enrot ) * 60 : fan.speed * -1 ) : + 0.f ) }; + + if( std::abs( fan.revolutions - revolutionstarget ) < 0.01f ) { + fan.revolutions = revolutionstarget; + continue; + } + if( revolutionstarget > 0.f ) { + auto const speedincreasecap { std::max( 50.f, fan.speed * 0.05f * -1 ) }; // 5% of fixed revolution speed, or 50 + fan.revolutions += clamp( revolutionstarget - fan.revolutions, speedincreasecap * -2, speedincreasecap ) * Timestep; + } + else { + fan.revolutions *= std::max( 0.0, 1.0 - Timestep ); + } } - OilPump.pressure_present = clamp( OilPump.pressure_present, 0.f, 1.5f ); } @@ -2608,6 +2647,60 @@ bool TMoverParameters::OilPumpSwitchOff( bool State, range_t const Notify ) { return ( OilPump.is_disabled != initialstate ); } +bool TMoverParameters::MotorBlowersSwitch( bool State, side const Side, range_t const Notify ) { + + auto &fan { MotorBlowers[ Side ] }; + + if( ( fan.start_type != start_t::manual ) + && ( fan.start_type != start_t::manualwithautofallback ) ) { + // automatic device ignores 'manual' state commands + return false; + } + + bool const initialstate { fan.is_enabled }; + + fan.is_enabled = State; + + if( Notify != range_t::local ) { + SendCtrlToNext( + ( Side == side::front ? "MotorBlowersFrontSwitch" : "MotorBlowersRearSwitch" ), + ( fan.is_enabled ? 1 : 0 ), + CabNo, + ( Notify == range_t::unit ? + coupling::control | coupling::permanent : + coupling::control ) ); + } + + return ( fan.is_enabled != initialstate ); +} + +bool TMoverParameters::MotorBlowersSwitchOff( bool State, side const Side, range_t const Notify ) { + + auto &fan { MotorBlowers[ Side ] }; + + if( ( fan.start_type != start_t::manual ) + && ( fan.start_type != start_t::manualwithautofallback ) ) { + // automatic device ignores 'manual' state commands + return false; + } + + bool const initialstate { fan.is_disabled }; + + fan.is_disabled = State; + + if( Notify != range_t::local ) { + SendCtrlToNext( + ( Side == side::front ? "MotorBlowersFrontSwitchOff" : "MotorBlowersRearSwitchOff" ), + ( fan.is_disabled ? 1 : 0 ), + CabNo, + ( Notify == range_t::unit ? + coupling::control | coupling::permanent : + coupling::control ) ); + } + + return ( fan.is_disabled != initialstate ); +} + // ************************************************************************************************* // Q: 20160713 // włączenie / wyłączenie obwodu głownego @@ -6022,7 +6115,7 @@ bool TMoverParameters::dizel_StartupCheck() { } // test the oil pump if( ( false == OilPump.is_active ) - || ( OilPump.pressure_present < OilPump.pressure_minimum ) ) { + || ( OilPump.pressure < OilPump.pressure_minimum ) ) { engineisready = false; if( OilPump.start_type == start_t::manual ) { // with manual pump control startup procedure is done only once per starter switch press @@ -8208,59 +8301,54 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) { } extract_value( ConverterStartDelay, "ConverterStartDelay", line, "" ); + // devices + std::map starts { + { "Manual", start_t::manual }, + { "Automatic", start_t::automatic }, + { "Mixed", start_t::manualwithautofallback }, + { "Battery", start_t::battery }, + { "Converter", start_t::converter } }; // compressor { - std::map starts { - { "Manual", start_t::manual }, - { "Automatic", start_t::automatic } - }; auto lookup = starts.find( extract_value( "CompressorStart", line ) ); CompressorStart = lookup != starts.end() ? lookup->second : start_t::manual; } - // fuel pump { - std::map starts { - { "Manual", start_t::manual }, - { "Automatic", start_t::automatic }, - { "Mixed", start_t::manualwithautofallback } - }; auto lookup = starts.find( extract_value( "FuelStart", line ) ); FuelPump.start_type = lookup != starts.end() ? lookup->second : start_t::manual; } - // oil pump { - std::map starts { - { "Manual", start_t::manual }, - { "Automatic", start_t::automatic }, - { "Mixed", start_t::manualwithautofallback } - }; auto lookup = starts.find( extract_value( "OilStart", line ) ); OilPump.start_type = lookup != starts.end() ? lookup->second : start_t::manual; } - // water pump { - std::map starts { - { "Manual", start_t::manual }, - { "Battery", start_t::battery } - }; auto lookup = starts.find( extract_value( "WaterStart", line ) ); WaterPump.start_type = lookup != starts.end() ? lookup->second : start_t::manual; } + // traction motor fans + { + auto lookup = starts.find( extract_value( "MotorBlowersStart", line ) ); + MotorBlowers[side::front].start_type = + MotorBlowers[side::rear].start_type = + lookup != starts.end() ? + lookup->second : + start_t::manual; + } } void TMoverParameters::LoadFIZ_Blending(std::string const &line) { @@ -8513,6 +8601,10 @@ void TMoverParameters::LoadFIZ_Engine( std::string const &Input ) { extract_value( WaterHeater.config.temp_min, "HeaterMinTemperature", Input, "" ); extract_value( WaterHeater.config.temp_max, "HeaterMaxTemperature", Input, "" ); } + + // traction motors + extract_value( MotorBlowers[ side::front ].speed, "MotorBlowersSpeed", Input, "" ); + MotorBlowers[ side::rear ] = MotorBlowers[ side::front ]; } void TMoverParameters::LoadFIZ_Switches( std::string const &Input ) { @@ -9289,7 +9381,39 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C } OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); } - else if (Command == "MainSwitch") + else if( Command == "MotorBlowersFrontSwitch" ) { + if( ( MotorBlowers[ side::front ].start_type != start_t::manual ) + && ( MotorBlowers[ side::front ].start_type != start_t::manualwithautofallback ) ) { + // automatic device ignores 'manual' state commands + MotorBlowers[side::front].is_enabled = ( CValue1 == 1 ); + } + OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); + } + else if( Command == "MotorBlowersFrontSwitchOff" ) { + if( ( MotorBlowers[ side::front ].start_type != start_t::manual ) + && ( MotorBlowers[ side::front ].start_type != start_t::manualwithautofallback ) ) { + // automatic device ignores 'manual' state commands + MotorBlowers[side::front].is_disabled = ( CValue1 == 1 ); + } + OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); + } + else if( Command == "MotorBlowersRearSwitch" ) { + if( ( MotorBlowers[ side::rear ].start_type != start_t::manual ) + && ( MotorBlowers[ side::rear ].start_type != start_t::manualwithautofallback ) ) { + // automatic device ignores 'manual' state commands + MotorBlowers[side::rear].is_enabled = ( CValue1 == 1 ); + } + OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); + } + else if( Command == "MotorBlowersRearSwitchOff" ) { + if( ( MotorBlowers[ side::rear ].start_type != start_t::manual ) + && ( MotorBlowers[ side::rear ].start_type != start_t::manualwithautofallback ) ) { + // automatic device ignores 'manual' state commands + MotorBlowers[side::rear].is_disabled = ( CValue1 == 1 ); + } + OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); + } + else if (Command == "MainSwitch") { if (CValue1 == 1) { diff --git a/Train.cpp b/Train.cpp index 22ff5ab3..e9b0312a 100644 --- a/Train.cpp +++ b/Train.cpp @@ -266,6 +266,9 @@ TTrain::commandhandler_map const TTrain::m_commandhandlers = { { user_command::compressorenable, &TTrain::OnCommand_compressorenable }, { user_command::compressordisable, &TTrain::OnCommand_compressordisable }, { user_command::compressortogglelocal, &TTrain::OnCommand_compressortogglelocal }, + { user_command::motorblowerstogglefront, &TTrain::OnCommand_motorblowerstogglefront }, + { user_command::motorblowerstogglerear, &TTrain::OnCommand_motorblowerstogglerear }, + { user_command::motorblowersdisableall, &TTrain::OnCommand_motorblowersdisableall }, { user_command::motorconnectorsopen, &TTrain::OnCommand_motorconnectorsopen }, { user_command::motorconnectorsclose, &TTrain::OnCommand_motorconnectorsclose }, { user_command::motordisconnect, &TTrain::OnCommand_motordisconnect }, @@ -2770,6 +2773,191 @@ void TTrain::OnCommand_compressortogglelocal( TTrain *Train, command_data const } } +void TTrain::OnCommand_motorblowerstogglefront( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_REPEAT ) { return; } + + if( Train->ggMotorBlowersFrontButton.type() == TGaugeType::push ) { + // impulse switch + // currently there's no off button so we always try to turn it on + OnCommand_motorblowersenablefront( Train, Command ); + } + else { + // two-state switch + if( Command.action == GLFW_RELEASE ) { return; } + + if( false == Train->mvControlled->MotorBlowers[side::front].is_enabled ) { + // turn on + OnCommand_motorblowersenablefront( Train, Command ); + } + else { + //turn off + OnCommand_motorblowersdisablefront( Train, Command ); + } + } +} + +void TTrain::OnCommand_motorblowersenablefront( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_REPEAT ) { return; } + + if( Train->ggMotorBlowersFrontButton.type() == TGaugeType::push ) { + // impulse switch + if( Command.action == GLFW_PRESS ) { + // visual feedback + Train->ggMotorBlowersFrontButton.UpdateValue( 1.f, Train->dsbSwitch ); + Train->mvControlled->MotorBlowersSwitch( true, side::front ); + } + else if( Command.action == GLFW_RELEASE ) { + // visual feedback + Train->ggMotorBlowersFrontButton.UpdateValue( 0.f, Train->dsbSwitch ); + Train->mvControlled->MotorBlowersSwitch( false, side::front ); + } + } + else { + // two-state switch, only cares about press events + if( Command.action == GLFW_PRESS ) { + // visual feedback + Train->ggMotorBlowersFrontButton.UpdateValue( 1.f, Train->dsbSwitch ); + Train->mvControlled->MotorBlowersSwitch( true, side::front ); + Train->mvControlled->MotorBlowersSwitchOff( false, side::front ); + } + } +} + +void TTrain::OnCommand_motorblowersdisablefront( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_REPEAT ) { return; } + + if( Train->ggMotorBlowersFrontButton.type() == TGaugeType::push ) { + // impulse switch + // currently there's no disable return type switch + return; + } + else { + // two-state switch, only cares about press events + if( Command.action == GLFW_PRESS ) { + // visual feedback + Train->ggMotorBlowersFrontButton.UpdateValue( 0.f, Train->dsbSwitch ); + Train->mvControlled->MotorBlowersSwitch( false, side::front ); + Train->mvControlled->MotorBlowersSwitchOff( true, side::front ); + } + } +} + +void TTrain::OnCommand_motorblowerstogglerear( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_REPEAT ) { return; } + + if( Train->ggMotorBlowersRearButton.type() == TGaugeType::push ) { + // impulse switch + // currently there's no off button so we always try to turn it on + OnCommand_motorblowersenablerear( Train, Command ); + } + else { + // two-state switch + if( Command.action == GLFW_RELEASE ) { return; } + + if( false == Train->mvControlled->MotorBlowers[ side::rear ].is_enabled ) { + // turn on + OnCommand_motorblowersenablerear( Train, Command ); + } + else { + //turn off + OnCommand_motorblowersdisablerear( Train, Command ); + } + } +} + +void TTrain::OnCommand_motorblowersenablerear( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_REPEAT ) { return; } + + if( Train->ggMotorBlowersRearButton.type() == TGaugeType::push ) { + // impulse switch + if( Command.action == GLFW_PRESS ) { + // visual feedback + Train->ggMotorBlowersRearButton.UpdateValue( 1.f, Train->dsbSwitch ); + Train->mvControlled->MotorBlowersSwitch( true, side::rear ); + } + else if( Command.action == GLFW_RELEASE ) { + // visual feedback + Train->ggMotorBlowersRearButton.UpdateValue( 0.f, Train->dsbSwitch ); + Train->mvControlled->MotorBlowersSwitch( false, side::rear ); + } + } + else { + // two-state switch, only cares about press events + if( Command.action == GLFW_PRESS ) { + // visual feedback + Train->ggMotorBlowersRearButton.UpdateValue( 1.f, Train->dsbSwitch ); + Train->mvControlled->MotorBlowersSwitch( true, side::rear ); + Train->mvControlled->MotorBlowersSwitchOff( false, side::rear ); + } + } +} + +void TTrain::OnCommand_motorblowersdisablerear( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_REPEAT ) { return; } + + if( Train->ggMotorBlowersRearButton.type() == TGaugeType::push ) { + // impulse switch + // currently there's no disable return type switch + return; + } + else { + // two-state switch, only cares about press events + if( Command.action == GLFW_PRESS ) { + // visual feedback + Train->ggMotorBlowersRearButton.UpdateValue( 0.f, Train->dsbSwitch ); + Train->mvControlled->MotorBlowersSwitch( false, side::rear ); + Train->mvControlled->MotorBlowersSwitchOff( true, side::rear ); + } + } +} + +void TTrain::OnCommand_motorblowersdisableall( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_REPEAT ) { return; } + + if( Train->ggMotorBlowersAllOffButton.type() == TGaugeType::push ) { + // impulse switch + if( Command.action == GLFW_PRESS ) { + // visual feedback + Train->ggMotorBlowersAllOffButton.UpdateValue( 1.f, Train->dsbSwitch ); + Train->mvControlled->MotorBlowersSwitchOff( true, side::front ); + Train->mvControlled->MotorBlowersSwitchOff( true, side::rear ); + } + else if( Command.action == GLFW_RELEASE ) { + // visual feedback + Train->ggMotorBlowersAllOffButton.UpdateValue( 0.f, Train->dsbSwitch ); + Train->mvControlled->MotorBlowersSwitchOff( false, side::front ); + Train->mvControlled->MotorBlowersSwitchOff( false, side::rear ); + } + } + else { + // two-state switch, only cares about press events + // NOTE: generally this switch doesn't come in two-state form + if( Command.action == GLFW_PRESS ) { + if( Train->ggMotorBlowersAllOffButton.GetDesiredValue() < 0.5f ) { + // switch is off, activate + Train->mvControlled->MotorBlowersSwitchOff( true, side::front ); + Train->mvControlled->MotorBlowersSwitchOff( true, side::rear ); + // visual feedback + Train->ggMotorBlowersRearButton.UpdateValue( 1.f, Train->dsbSwitch ); + } + else { + // deactivate + Train->mvControlled->MotorBlowersSwitchOff( false, side::front ); + Train->mvControlled->MotorBlowersSwitchOff( false, side::rear ); + // visual feedback + Train->ggMotorBlowersRearButton.UpdateValue( 0.f, Train->dsbSwitch ); + } + } + } +} + void TTrain::OnCommand_motorconnectorsopen( TTrain *Train, command_data const &Command ) { // TODO: don't rely on presense of 3d model to determine presence of the switch @@ -5043,7 +5231,7 @@ bool TTrain::Update( double const Deltatime ) ggWater1TempB.Update(); } if( ggOilPressB.SubModel ) { - ggOilPressB.UpdateValue( tmp->MoverParameters->OilPump.pressure_present ); + ggOilPressB.UpdateValue( tmp->MoverParameters->OilPump.pressure ); ggOilPressB.Update(); } } @@ -5344,7 +5532,7 @@ bool TTrain::Update( double const Deltatime ) btLampkaRearRightEndLight.Turn( ( mvOccupied->iLights[ side::rear ] & light::redmarker_right ) != 0 ); // others btLampkaMalfunction.Turn( mvControlled->dizel_heat.PA ); - btLampkaMotorBlowers.Turn( mvControlled->RventRot > 0.1 ); + btLampkaMotorBlowers.Turn( ( mvControlled->MotorBlowers[ side::front ].is_active ) && ( mvControlled->MotorBlowers[ side::rear ].is_active ) ); } else { // wylaczone @@ -5704,6 +5892,9 @@ bool TTrain::Update( double const Deltatime ) ggWaterCircuitsLinkButton.Update(); ggFuelPumpButton.Update(); ggOilPumpButton.Update(); + ggMotorBlowersFrontButton.Update(); + ggMotorBlowersRearButton.Update(); + ggMotorBlowersAllOffButton.Update(); //------ } // wyprowadzenie sygnałów dla haslera na PoKeys (zaznaczanie na taśmie) @@ -6927,6 +7118,9 @@ void TTrain::clear_cab_controls() ggWaterCircuitsLinkButton.Clear(); ggFuelPumpButton.Clear(); ggOilPumpButton.Clear(); + ggMotorBlowersFrontButton.Clear(); + ggMotorBlowersRearButton.Clear(); + ggMotorBlowersAllOffButton.Clear(); btLampkaPrzetw.Clear(); btLampkaPrzetwB.Clear(); @@ -7271,6 +7465,26 @@ void TTrain::set_cab_controls() { 1.0 : 0.0 ); } + // traction motor fans + if( ggMotorBlowersFrontButton.type() != TGaugeType::push ) { + ggMotorBlowersFrontButton.PutValue( + mvControlled->MotorBlowers[side::front].is_enabled ? + 1.0 : + 0.0 ); + } + if( ggMotorBlowersRearButton.type() != TGaugeType::push ) { + ggMotorBlowersRearButton.PutValue( + mvControlled->MotorBlowers[side::rear].is_enabled ? + 1.0 : + 0.0 ); + } + if( ggMotorBlowersAllOffButton.type() != TGaugeType::push ) { + ggMotorBlowersAllOffButton.PutValue( + ( mvControlled->MotorBlowers[side::front].is_disabled + || mvControlled->MotorBlowers[ side::front ].is_disabled ) ? + 1.0 : + 0.0 ); + } // we reset all indicators, as they're set during the update pass // TODO: when cleaning up break setting indicator state into a separate function, so we can reuse it @@ -7465,6 +7679,9 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con { "fuelpump_sw:", ggFuelPumpButton }, { "oilpump_sw:", ggOilPumpButton }, { "oilpressb:", ggOilPressB }, + { "motorblowersfront_sw:", ggMotorBlowersFrontButton }, + { "motorblowersrear_sw:", ggMotorBlowersRearButton }, + { "motorblowersalloff_sw:", ggMotorBlowersAllOffButton }, { "radio_sw:", ggRadioButton }, { "radiochannel_sw:", ggRadioChannelSelector }, { "radiochannelprev_sw:", ggRadioChannelPrevious }, @@ -7630,7 +7847,7 @@ 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( &mvControlled->OilPump.pressure_present ); + gauge.AssignFloat( &mvControlled->OilPump.pressure ); } else if( Label == "oiltemp:" ) { // oil temperature diff --git a/Train.h b/Train.h index 2a84462d..dd6296dd 100644 --- a/Train.h +++ b/Train.h @@ -251,6 +251,13 @@ class TTrain static void OnCommand_compressorenable( TTrain *Train, command_data const &Command ); static void OnCommand_compressordisable( TTrain *Train, command_data const &Command ); static void OnCommand_compressortogglelocal( TTrain *Train, command_data const &Command ); + static void OnCommand_motorblowerstogglefront( TTrain *Train, command_data const &Command ); + static void OnCommand_motorblowersenablefront( TTrain *Train, command_data const &Command ); + static void OnCommand_motorblowersdisablefront( TTrain *Train, command_data const &Command ); + static void OnCommand_motorblowerstogglerear( TTrain *Train, command_data const &Command ); + static void OnCommand_motorblowersenablerear( TTrain *Train, command_data const &Command ); + static void OnCommand_motorblowersdisablerear( TTrain *Train, command_data const &Command ); + static void OnCommand_motorblowersdisableall( TTrain *Train, command_data const &Command ); static void OnCommand_motorconnectorsopen( TTrain *Train, command_data const &Command ); static void OnCommand_motorconnectorsclose( TTrain *Train, command_data const &Command ); static void OnCommand_motordisconnect( TTrain *Train, command_data const &Command ); @@ -322,6 +329,7 @@ class TTrain static void OnCommand_cabchangebackward( TTrain *Train, command_data const &Command ); static void OnCommand_generictoggle( TTrain *Train, command_data const &Command ); + // members TDynamicObject *DynamicObject { nullptr }; // przestawia zmiana pojazdu [F5] TMoverParameters *mvControlled { nullptr }; // człon, w którym sterujemy silnikiem @@ -350,7 +358,7 @@ public: // reszta może by?publiczna TGauge ggI3B; TGauge ggItotalB; - TGauge ggOilPressB; + TGauge ggOilPressB; // other unit oil pressure indicator TGauge ggWater1TempB; // McZapkie: definicje regulatorow @@ -458,6 +466,9 @@ public: // reszta może by?publiczna TGauge ggWaterCircuitsLinkButton; TGauge ggFuelPumpButton; // fuel pump switch TGauge ggOilPumpButton; // fuel pump switch + TGauge ggMotorBlowersFrontButton; // front traction motor fan switch + TGauge ggMotorBlowersRearButton; // rear traction motor fan switch + TGauge ggMotorBlowersAllOffButton; // motor fans shutdown switch TButton btLampkaPoslizg; TButton btLampkaStyczn; diff --git a/command.cpp b/command.cpp index d1e7e768..6edade69 100644 --- a/command.cpp +++ b/command.cpp @@ -214,10 +214,14 @@ commanddescription_sequence Commands_descriptions = { { "generictoggle9", command_target::vehicle }, { "batterytoggle", command_target::vehicle }, { "batteryenable", command_target::vehicle }, - { "batterydisable", command_target::vehicle } + { "batterydisable", command_target::vehicle }, + { "motorblowerstogglefront", command_target::vehicle }, + { "motorblowerstogglerear", command_target::vehicle }, + { "motorblowersdisableall", command_target::vehicle } + }; -} +} // simulation // posts specified command for specified recipient void diff --git a/command.h b/command.h index e6048bee..5f34b6fd 100644 --- a/command.h +++ b/command.h @@ -208,6 +208,9 @@ enum class user_command { batterytoggle, batteryenable, batterydisable, + motorblowerstogglefront, + motorblowerstogglerear, + motorblowersdisableall, none = -1 }; diff --git a/driverkeyboardinput.cpp b/driverkeyboardinput.cpp index efe49008..ae8aab93 100644 --- a/driverkeyboardinput.cpp +++ b/driverkeyboardinput.cpp @@ -217,6 +217,10 @@ driverkeyboard_input::default_bindings() { { user_command::batterytoggle, GLFW_KEY_J }, // batteryenable, // batterydisable, + { user_command::motorblowerstogglefront, GLFW_KEY_N | keymodifier::shift }, + { user_command::motorblowerstogglerear, GLFW_KEY_M | keymodifier::shift }, + { user_command::motorblowersdisableall, GLFW_KEY_M | keymodifier::control } + }; } diff --git a/drivermouseinput.cpp b/drivermouseinput.cpp index 235bc589..0a34c2f2 100644 --- a/drivermouseinput.cpp +++ b/drivermouseinput.cpp @@ -499,6 +499,15 @@ drivermouse_input::default_bindings() { { "oilpump_sw:", { user_command::oilpumptoggle, user_command::none } }, + { "motorblowersfront_sw:", { + user_command::motorblowerstogglefront, + user_command::none } }, + { "motorblowersrear_sw:", { + user_command::motorblowerstogglerear, + user_command::none } }, + { "motorblowersalloff_sw:", { + user_command::motorblowersdisableall, + user_command::none } }, { "main_off_bt:", { user_command::linebreakeropen, user_command::none } }, diff --git a/driveruipanels.cpp b/driveruipanels.cpp index 84b930f6..ceb9253f 100644 --- a/driveruipanels.cpp +++ b/driveruipanels.cpp @@ -414,7 +414,7 @@ debug_panel::update_section_vehicle( std::vector &Output ) { ( mover.CompressorFlag ? 'C' : ( false == mover.CompressorAllowLocal ? '-' : ( ( mover.CompressorAllow || mover.CompressorStart == start_t::automatic ) ? 'c' : '.' ) ) ), ( mover.CompressorGovernorLock ? '!' : '.' ), std::string( isplayervehicle ? locale::strings[ locale::string::debug_vehicle_radio ] + ( mover.Radio ? std::to_string( m_input.train->RadioChannel() ) : "-" ) : "" ).c_str(), - std::string( isdieselenginepowered ? locale::strings[ locale::string::debug_vehicle_oilpressure ] + to_string( mover.OilPump.pressure_present, 2 ) : "" ).c_str(), + std::string( isdieselenginepowered ? locale::strings[ locale::string::debug_vehicle_oilpressure ] + to_string( mover.OilPump.pressure, 2 ) : "" ).c_str(), // power transfers mover.Couplers[ side::front ].power_high.voltage, mover.Couplers[ side::front ].power_high.current, @@ -440,6 +440,8 @@ debug_panel::update_section_vehicle( std::vector &Output ) { std::abs( mover.enrot ) * 60, std::abs( mover.nrot ) * mover.Transmision.Ratio * 60, mover.RventRot * 60, + mover.MotorBlowers[side::front].revolutions, + mover.MotorBlowers[side::rear].revolutions, mover.dizel_heat.rpmw, mover.dizel_heat.rpmw2 ); diff --git a/translation.cpp b/translation.cpp index 52f255da..c9bc66f9 100644 --- a/translation.cpp +++ b/translation.cpp @@ -54,7 +54,7 @@ init() { "Devices: %c%c%c%c%c%c%c%c%c%c%c%c%c%c%s%s\nPower transfers: %.0f@%.0f%s%s%s%.0f@%.0f", " radio: ", " oil pressure: ", - "Controllers:\n master: %d(%d), secondary: %s\nEngine output: %.1f, current: %.0f\nRevolutions:\n engine: %.0f, motors: %.0f, ventilators: %.0f, fans: %.0f+%.0f", + "Controllers:\n master: %d(%d), secondary: %s\nEngine output: %.1f, current: %.0f\nRevolutions:\n engine: %.0f, motors: %.0f\n engine fans: %.0f, motor fans: %.0f+%.0f, cooling fans: %.0f+%.0f", " (shunt mode)", "\nTemperatures:\n engine: %.2f, oil: %.2f, water: %.2f%c%.2f", "Brakes:\n train: %.2f, independent: %.2f, delay: %s, load flag: %d\nBrake cylinder pressures:\n train: %.2f, independent: %.2f, status: 0x%.2x\nPipe pressures:\n brake: %.2f (hat: %.2f), main: %.2f, control: %.2f\nTank pressures:\n auxiliary: %.2f, main: %.2f, control: %.2f", @@ -80,6 +80,9 @@ init() { "water circuits link", "fuel pump", "oil pump", + "motor blowers A", + "motor blowers B", + "all motor blowers", "line breaker", "line breaker", "alerter", @@ -188,7 +191,7 @@ init() { "Urzadzenia: %c%c%c%c%c%c%c%c%c%c%c%c%c%c%s%s\nTransfer pradow: %.0f@%.0f%s%s%s%.0f@%.0f", " radio: ", " cisn.oleju: ", - "Nastawniki:\n glowny: %d(%d), dodatkowy: %s\nMoc silnika: %.1f, prad silnika: %.0f\nObroty:\n silnik: %.0f, motory: %.0f, went.silnika: %.0f, went.chlodnicy: %.0f+%.0f", + "Nastawniki:\n glowny: %d(%d), dodatkowy: %s\nMoc silnika: %.1f, prad silnika: %.0f\nObroty:\n silnik: %.0f, motory: %.0f\n went.silnika: %.0f, went.motorow: %.0f+%.0f, went.chlodnicy: %.0f+%.0f", " (tryb manewrowy)", "\nTemperatury:\n silnik: %.2f, olej: %.2f, woda: %.2f%c%.2f", "Hamulce:\n zespolony: %.2f, pomocniczy: %.2f, nastawa: %s, ladunek: %d\nCisnienie w cylindrach:\n zespolony: %.2f, pomocniczy: %.2f, status: 0x%.2x\nCisnienia w przewodach:\n glowny: %.2f (kapturek: %.2f), zasilajacy: %.2f, kontrolny: %.2f\nCisnienia w zbiornikach:\n pomocniczy: %.2f, glowny: %.2f, sterujacy: %.2f", @@ -214,6 +217,9 @@ init() { "zawor polaczenia obiegow wody", "pompa paliwa", "pompa oleju", + "wentylatory silnikow trakcyjnych A", + "wentylatory silnikow trakcyjnych B", + "wszystkie wentylatory silnikow trakcyjnych", "wylacznik szybki", "wylacznik szybki", "czuwak", @@ -320,6 +326,9 @@ init() { "watercircuitslink_sw:", "fuelpump_sw:", "oilpump_sw:", + "motorblowersfront_sw:", + "motorblowersrear_sw:", + "motorblowersalloff_sw:", "main_off_bt:", "main_on_bt:", "security_reset_bt:", diff --git a/translation.h b/translation.h index 2c39471e..4d40b762 100644 --- a/translation.h +++ b/translation.h @@ -69,6 +69,9 @@ enum string { cab_watercircuitslink_sw, cab_fuelpump_sw, cab_oilpump_sw, + cab_motorblowersfront_sw, + cab_motorblowersrear_sw, + cab_motorblowersalloff_sw, cab_main_off_bt, cab_main_on_bt, cab_security_reset_bt,