diff --git a/Driver.cpp b/Driver.cpp index 050e43d6..a70ef340 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -1390,6 +1390,12 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN fVelDes = v; } } + if( ( true == TestFlag( sSpeedTable[ i ].iFlags, spEnd ) ) + && ( mvOccupied->CategoryFlag & 1 ) ) { + // if the railway track ends here set the velnext accordingly as well + // TODO: test this with turntables and such + fNext = 0.0; + } } else if (sSpeedTable[i].iFlags & spTrack) // jeśli tor { // tor ogranicza prędkość, dopóki cały skład nie przejedzie, @@ -1401,12 +1407,6 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN // ograniczenie aktualnej prędkości aż do wyjechania za ograniczenie fVelDes = v; } - if( ( sSpeedTable[ i ].iFlags & spEnd ) - && ( mvOccupied->CategoryFlag & 1 ) ) { - // if the railway track ends here set the velnext accordingly as well - // TODO: test this with turntables and such - fNext = 0.0; - } // if (v==0.0) fAcc=-0.9; //hamowanie jeśli stop continue; // i tyle wystarczy } @@ -2129,7 +2129,10 @@ bool TController::CheckVehicles(TOrders user) // NOTE: don't set battery in the occupied vehicle, let the user/ai do it explicitly p->MoverParameters->BatterySwitch( true ); } - // enable heating and converter in carriages with can be heated + } + // enable heating and converter in carriages with can be heated + // NOTE: don't touch the controlled vehicle, let the user/ai handle it explicitly + if( p->MoverParameters != mvControlling ) { if( p->MoverParameters->HeatingPower > 0 ) { p->MoverParameters->HeatingAllow = true; p->MoverParameters->ConverterSwitch( true, range_t::local ); @@ -2264,6 +2267,12 @@ bool TController::CheckVehicles(TOrders user) // zmiana czoła przez manewry iDrivigFlags &= ~movePushPull; } + + if( ( user == Connect ) + || ( user == Disconnect ) ) { + // HACK: force route table update on consist change, new consist length means distances to points of interest are now wrong + iTableDirection = 0; + } } // blok wykonywany, gdy aktywnie prowadzi return true; } @@ -2452,7 +2461,7 @@ bool TController::PrepareEngine() mvOccupied->PantRear( true ); if (mvControlling->PantPress < 4.2) { // załączenie małej sprężarki - if( mvControlling->TrainType != dt_EZT ) { + if( false == mvControlling->PantAutoValve ) { // odłączenie zbiornika głównego, bo z nim nie da rady napompować mvControlling->bPantKurek3 = false; } @@ -2525,14 +2534,14 @@ bool TController::PrepareEngine() } else { OK = ( OrderDirectionChange( iDirection, mvOccupied ) == -1 ); - mvOccupied->ConverterSwitch( true ); + mvControlling->ConverterSwitch( true ); // w EN57 sprężarka w ra jest zasilana z silnikowego - mvOccupied->CompressorSwitch( true ); + mvControlling->CompressorSwitch( true ); // enable motor blowers - mvOccupied->MotorBlowersSwitchOff( false, end::front ); - mvOccupied->MotorBlowersSwitch( true, end::front ); - mvOccupied->MotorBlowersSwitchOff( false, end::rear ); - mvOccupied->MotorBlowersSwitch( true, end::rear ); + mvControlling->MotorBlowersSwitchOff( false, end::front ); + mvControlling->MotorBlowersSwitch( true, end::front ); + mvControlling->MotorBlowersSwitchOff( false, end::rear ); + mvControlling->MotorBlowersSwitch( true, end::rear ); // enable train brake if it's off if( mvOccupied->fBrakeCtrlPos == mvOccupied->Handle->GetPos( bh_NP ) ) { mvOccupied->BrakeLevelSet( mvOccupied->Handle->GetPos( bh_RP ) ); @@ -2546,6 +2555,14 @@ bool TController::PrepareEngine() if( lookup != brakepositions.end() ) { BrakeLevelSet( lookup->second ); // GBH } + // enable train heating + // HACK: to account for su-45/-46 shortcomings diesel-powered engines only activate heating in cold conditions + // TODO: take instead into account presence of converters in attached cars, once said presence is possible to specify + mvControlling->HeatingAllow = ( + ( ( mvControlling->EngineType == TEngineType::DieselElectric ) + || ( mvControlling->EngineType == TEngineType::DieselEngine ) ) ? + ( Global.AirTemperature < 10 ) : + true ); } } else @@ -2628,7 +2645,9 @@ bool TController::ReleaseEngine() { mvOccupied->OperateDoors( side::left, false ); if( true == mvControlling->Mains ) { - mvControlling->CompressorSwitch( false ); + // heating + mvControlling->HeatingAllow = false; + // devices mvControlling->ConverterSwitch( false ); // line breaker/engine OK = mvControlling->MainSwitch( false ); @@ -3123,6 +3142,14 @@ bool TController::IncSpeed() { // dla 2Ls150 można zmienić tryb pracy, jeśli jest w liniowym i nie daje rady (wymaga zerowania kierunku) // mvControlling->ShuntMode=(OrderList[OrderPos]&Shunt)||(fMass>224000.0); } + if (mvControlling->EIMCtrlType > 0) { + if (true == Ready) + { + DizelPercentage = (mvControlling->Vel > mvControlling->dizel_minVelfullengage ? 100 : 1); + } + break; + } + else if( true == Ready ) { if( ( mvControlling->Vel > mvControlling->dizel_minVelfullengage ) && ( mvControlling->RList[ mvControlling->MainCtrlPos ].Mn > 0 ) ) { @@ -3185,6 +3212,12 @@ bool TController::DecSpeed(bool force) mvControlling->DecMainCtrl(3 + 3 * floor(0.5 + fabs(AccDesired))); break; case TEngineType::DieselEngine: + if (mvControlling->EIMCtrlType > 0) + { + DizelPercentage = 0; + break; + } + if ((mvControlling->Vel > mvControlling->dizel_minVelfullengage)) { if (mvControlling->RList[mvControlling->MainCtrlPos].Mn > 0) @@ -3508,6 +3541,64 @@ void TController::SetTimeControllers() } } //5. Check Main Controller in Dizels + //5.1. Digital controller in DMUs with hydro + if ((mvControlling->EngineType == TEngineType::DieselEngine) && (mvControlling->EIMCtrlType == 3)) + { + + DizelPercentage_Speed = DizelPercentage; //wstepnie procenty + auto MinVel{ std::min(mvControlling->hydro_TC_LockupSpeed, mvControlling->Vmax / 6) }; //minimal velocity + if (VelDesired > MinVel) //more power for faster ride + { + auto const Factor{ 10 * (mvControlling->Vmax) / (mvControlling->Vmax + 3 * mvControlling->Vel) }; + auto DesiredPercentage{ clamp( + (VelDesired > mvControlling->Vel ? + (VelDesired - mvControlling->Vel) / Factor : + 0), + 0.0, 1.0) }; //correction for reaching desired velocity + if ((VelDesired < 0.5 * mvControlling->Vmax) //low velocity and reaching desired + && (VelDesired - mvControlling->Vel < 10)) { + DesiredPercentage = std::min(DesiredPercentage, 0.75); + } + DizelPercentage_Speed = std::round(DesiredPercentage * DizelPercentage); + } + else + { + DizelPercentage_Speed = std::min(DizelPercentage, mvControlling->Vel < 0.99 * VelDesired ? 1 : 0); + } + + auto const DizelActualPercentage { int(100.4 * mvControlling->eimic_real) }; + + auto const PosInc { mvControlling->MainCtrlPosNo }; + auto PosDec { 0 }; + for( int i = PosInc; i >= 0; --i ) { + if( ( mvControlling->UniCtrlList[ i ].SetCtrlVal <= 0 ) + && ( mvControlling->UniCtrlList[ i ].SpeedDown > 0.01 ) ) { + PosDec = i; + break; + } + } + + if( std::abs( DizelPercentage_Speed - DizelActualPercentage ) > ( DizelPercentage > 1 ? 0 : 0 ) ) { + + if( ( PosDec > 0 ) + && ( ( DizelActualPercentage - DizelPercentage_Speed > 50 ) + || ( ( DizelPercentage_Speed == 0 ) + && ( DizelActualPercentage > 10 ) ) ) ) { + //one position earlier should be fast decreasing + PosDec -= 1; + } + auto const DesiredPos { ( + DizelPercentage_Speed > DizelActualPercentage ? + PosInc : + PosDec ) }; + while( mvControlling->MainCtrlPos > DesiredPos ) { mvControlling->DecMainCtrl( 1 ); } + while( mvControlling->MainCtrlPos < DesiredPos ) { mvControlling->IncMainCtrl( 1 ); } + } + if (BrakeCtrlPosition < 0.1) //jesli nie hamuje + mvOccupied->BrakeLevelSet(mvControlling->UniCtrlList[mvControlling->MainCtrlPos].mode); //zeby nie bruzdzilo machanie zespolonym + } + else + //5.2. Analog direct controller if ((mvControlling->EngineType == TEngineType::DieselEngine)&&(mvControlling->Vmax>30)) { int MaxPos = mvControlling->MainCtrlPosNo; @@ -3567,6 +3658,28 @@ void TController::CheckTimeControllers() mvOccupied->ScndCtrlPos = 2; } } + + //5. Check Main Controller in Dizels + //5.1. Digital controller in DMUs with hydro + if ((mvControlling->EngineType == TEngineType::DieselEngine) && (mvControlling->EIMCtrlType == 3)) + { + int DizelActualPercentage = 100.4 * mvControlling->eimic_real; + int NeutralPos = mvControlling->MainCtrlPosNo - 1; //przedostatnia powinna wstrzymywać - hipoteza robocza + for (int i = mvControlling->MainCtrlPosNo; i >= 0; i--) + if ((mvControlling->UniCtrlList[i].SetCtrlVal <= 0) && (mvControlling->UniCtrlList[i].SpeedDown < 0.01)) //niby zero, ale nie zmniejsza procentów + { + NeutralPos = i; + break; + } + if (BrakeCtrlPosition < 0.1) //jesli nie hamuje + { + if ((DizelActualPercentage >= DizelPercentage_Speed) && (mvControlling->MainCtrlPos > NeutralPos)) + while (mvControlling->MainCtrlPos > NeutralPos) mvControlling->DecMainCtrl(1); + if ((DizelActualPercentage <= DizelPercentage_Speed) && (mvControlling->MainCtrlPos < NeutralPos)) + while (mvControlling->MainCtrlPos < NeutralPos) mvControlling->IncMainCtrl(1); + mvOccupied->BrakeLevelSet(mvControlling->UniCtrlList[mvControlling->MainCtrlPos].mode); //zeby nie bruzdzilo machanie zespolonym + } + } }; // otwieranie/zamykanie drzwi w składzie albo (tylko AI) EZT @@ -4748,7 +4861,7 @@ TController::UpdateSituation(double dt) { iCoupler = 0; // dalsza jazda manewrowa już bez łączenia iDrivigFlags &= ~moveConnect; // zdjęcie flagi doczepiania SetVelocity( 0, 0, stopJoin ); // wyłączyć przyspieszanie - CheckVehicles(); // sprawdzić światła nowego składu + CheckVehicles( Connect ); // sprawdzić światła nowego składu JumpToNextOrder(); // wykonanie następnej komendy } } @@ -5115,6 +5228,7 @@ TController::UpdateSituation(double dt) { // tylko jeśli odepnie WriteLog( mvOccupied->Name + " odczepiony." ); iVehicleCount = -2; + CheckVehicles( Disconnect ); // update trainset state } // a jak nie, to dociskać dalej } if ((mvOccupied->Vel < 0.01) && !(iDrivigFlags & movePress)) @@ -5122,6 +5236,7 @@ TController::UpdateSituation(double dt) { // za radą yB ustawiamy pozycję 3 kranu (ruszanie kranem w innych miejscach // powino zostać wyłączone) // WriteLog("Zahamowanie składu"); + AccDesired = std::min( AccDesired, -0.9 ); // HACK: make sure the ai doesn't try to release the brakes to accelerate if( mvOccupied->BrakeSystem == TBrakeSystem::ElectroPneumatic ) { mvOccupied->BrakeLevelSet( mvOccupied->Handle->GetPos( bh_EPB ) ); } @@ -5369,7 +5484,8 @@ TController::UpdateSituation(double dt) { exchangetime = std::max( exchangetime, vehicle->LoadExchangeTime() ); vehicle = vehicle->Next(); } - if( exchangetime > 0 ) { + if( ( exchangetime > 0 ) + || ( mvOccupied->Vel > 2.0 ) ) { // HACK: force timer reset if the load exchange is cancelled due to departure WaitingSet( exchangetime ); } } diff --git a/Driver.h b/Driver.h index a521dcbe..86038dd0 100644 --- a/Driver.h +++ b/Driver.h @@ -265,6 +265,8 @@ public: int iOverheadZero = 0; // suma bitowa jezdy bezprądowej, bity ustawiane przez pojazdy z podniesionymi pantografami int iOverheadDown = 0; // suma bitowa opuszczenia pantografów, bity ustawiane przez pojazdy z podniesionymi pantografami double BrakeCtrlPosition = 0.0; // intermediate position of main brake controller + int DizelPercentage = 0; // oczekiwane procenty jazdy/hamowania szynobusem + int DizelPercentage_Speed = 0; // oczekiwane procenty jazdy/hamowania szynobusem w związku z osiąganiem VelDesired private: bool Psyche = false; int HelperState = 0; //stan pomocnika maszynisty diff --git a/DynObj.cpp b/DynObj.cpp index ac55b48e..8a6bc99c 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -2720,10 +2720,11 @@ bool TDynamicObject::Update(double dt, double dt1) if (v == 0.0) { v = MoverParameters->PantFrontVolt; if( v == 0.0 ) { - if( MoverParameters->TrainType & ( dt_EZT | dt_ET40 | dt_ET41 | dt_ET42 ) ) { +// if( MoverParameters->TrainType & ( dt_EZT | dt_ET40 | dt_ET41 | dt_ET42 ) ) { // dwuczłony mogą mieć sprzęg WN + // NOTE: condition disabled, other vehicles types can have power cables as well v = MoverParameters->GetTrainsetVoltage(); // ostatnia szansa - } +// } } } if (v != 0.0) @@ -2776,7 +2777,14 @@ bool TDynamicObject::Update(double dt, double dt1) if (Mechanik) { // Ra 2F3F: do Driver.cpp to przenieść? MoverParameters->EqvtPipePress = GetEPP(); // srednie cisnienie w PG - if( ( Mechanik->Primary() ) + if ((Mechanik->Primary()) + && (MoverParameters->EngineType == TEngineType::DieselEngine) + && (MoverParameters->EIMCtrlType > 0)) { + MoverParameters->CheckEIMIC(dt1); + MoverParameters->eimic_real = MoverParameters->eimic; + MoverParameters->SendCtrlToNext("EIMIC", MoverParameters->eimic, MoverParameters->CabNo); + } + if( ( Mechanik->Primary() ) && ( MoverParameters->EngineType == TEngineType::ElectricInductionMotor ) ) { // jesli glowny i z asynchronami, to niech steruje hamulcem i napedem lacznie dla calego pociagu/ezt auto const kier = (DirectionGet() * MoverParameters->ActiveCab > 0); @@ -5828,23 +5836,28 @@ TDynamicObject::LoadMMediaFile_mdload( std::string const &Name ) const { TModel3d *loadmodel { nullptr }; // check if we don't have model override for this load type - auto const lookup { LoadModelOverrides.find( Name ) }; - if( lookup != LoadModelOverrides.end() ) { - loadmodel = TModelsManager::GetModel( asBaseDir + lookup->second, true ); - // if the override was succesfully loaded call it a day - if( loadmodel != nullptr ) { return loadmodel; } + { + auto const lookup { LoadModelOverrides.find( Name ) }; + if( lookup != LoadModelOverrides.end() ) { + loadmodel = TModelsManager::GetModel( asBaseDir + lookup->second, true ); + // if the override was succesfully loaded call it a day + if( loadmodel != nullptr ) { return loadmodel; } + } } // regular routine if there's no override or it couldn't be loaded // try first specialized version of the load model, vehiclename_loadname - auto const specializedloadfilename { asBaseDir + MoverParameters->TypeName + "_" + Name }; - if( ( true == FileExists( specializedloadfilename + ".e3d" ) ) - || ( true == FileExists( specializedloadfilename + ".t3d" ) ) ) { - loadmodel = TModelsManager::GetModel( specializedloadfilename, true ); + { + auto const specializedloadfilename { asBaseDir + MoverParameters->TypeName + "_" + Name }; + loadmodel = TModelsManager::GetModel( specializedloadfilename, true, false ); + if( loadmodel != nullptr ) { return loadmodel; } } - if( loadmodel == nullptr ) { - // if this fails, try generic load model - loadmodel = TModelsManager::GetModel( asBaseDir + Name, true ); + // try generic version of the load model next, loadname + { + auto const genericloadfilename { asBaseDir + Name }; + loadmodel = TModelsManager::GetModel( genericloadfilename, true, false ); + if( loadmodel != nullptr ) { return loadmodel; } } + // if we're still here, give up return loadmodel; } @@ -6689,7 +6702,7 @@ TDynamicObject::powertrain_sounds::render( TMoverParameters const &Vehicle, doub // youBy - przenioslem, bo diesel tez moze miec turbo if( Vehicle.TurboTest > 0 ) { // udawanie turbo: - auto const pitch_diesel { Vehicle.EngineType == TEngineType::DieselEngine ? Vehicle.enrot / Vehicle.dizel_nmax : 0 }; + auto const pitch_diesel { Vehicle.EngineType == TEngineType::DieselEngine ? Vehicle.enrot / Vehicle.dizel_nmax : 1 }; auto const goalpitch { std::max( 0.025, ( engine_volume * pitch_diesel + engine_turbo.m_frequencyoffset ) * engine_turbo.m_frequencyfactor ) }; auto const goalvolume { ( ( ( Vehicle.MainCtrlPos >= Vehicle.TurboTest ) && ( Vehicle.enrot > 0.1 ) ) ? diff --git a/EvLaunch.cpp b/EvLaunch.cpp index ce3badf6..b6cb0539 100644 --- a/EvLaunch.cpp +++ b/EvLaunch.cpp @@ -59,12 +59,25 @@ bool TEventLauncher::Load(cParser *parser) dRadius *= dRadius; // do kwadratu, pod warunkiem, że nie jest ujemne parser->getTokens(); // klawisz sterujący *parser >> token; - if (token != "none") - { - if( token.size() == 1 ) + if (token != "none") { + + if( token.size() == 1 ) { + // single char, assigned activation key iKey = vk_to_glfw_key( token[ 0 ] ); - else - iKey = vk_to_glfw_key(stol_def( token, 0 )); // a jak więcej, to jakby numer klawisza jest + } + else { + // this launcher may be activated by radio message + std::map messages { + { "radio_call1", radio_message::call1 }, + { "radio_call3", radio_message::call3 } + }; + auto lookup = messages.find( token ); + iKey = ( + lookup != messages.end() ? + lookup->second : + // a jak więcej, to jakby numer klawisza jest + vk_to_glfw_key( stol_def( token, 0 ) ) ); + } } parser->getTokens(); *parser >> DeltaTime; @@ -139,6 +152,8 @@ bool TEventLauncher::Load(cParser *parser) } bool TEventLauncher::check_activation_key() { + if (iKey <= 0) + return false; char key = iKey & 0xff; @@ -146,9 +161,9 @@ bool TEventLauncher::check_activation_key() { char modifier = iKey >> 8; if (modifier & GLFW_MOD_SHIFT) - result |= Global.shiftState; + result &= Global.shiftState; if (modifier & GLFW_MOD_CONTROL) - result |= Global.ctrlState; + result &= Global.ctrlState; return result; } @@ -213,6 +228,11 @@ bool TEventLauncher::IsGlobal() const { && ( dRadius < 0.0 ) ); // bez ograniczenia zasięgu } +bool TEventLauncher::IsRadioActivated() const { + + return ( iKey < 0 ); +} + // radius() subclass details, calculates node's bounding radius float TEventLauncher::radius_() { diff --git a/EvLaunch.h b/EvLaunch.h index dc6ce410..7ed9d240 100644 --- a/EvLaunch.h +++ b/EvLaunch.h @@ -14,6 +14,12 @@ http://mozilla.org/MPL/2.0/. #include "Classes.h" #include "scenenode.h" +// radio-transmitted event launch messages +enum radio_message { + call3 = -3, + call1 = -1 +}; + class TEventLauncher : public scene::basic_node { public: @@ -28,7 +34,11 @@ public: bool check_activation(); // checks conditions associated with the event. returns: true if the conditions are met bool check_conditions(); + inline + auto key() const { + return iKey; } bool IsGlobal() const; + bool IsRadioActivated() const; // members std::string asEvent1Name; std::string asEvent2Name; diff --git a/Event.cpp b/Event.cpp index e721c680..df6f3281 100644 --- a/Event.cpp +++ b/Event.cpp @@ -1034,7 +1034,7 @@ logvalues_event::export_as_text_( std::ostream &Output ) const { void multi_event::init() { - auto const conditiontchecksmemcell { m_conditions.flags & ( flags::text | flags::value_1 | flags::value_2 ) }; + auto const conditiontchecksmemcell { ( m_conditions.flags & ( flags::text | flags::value_1 | flags::value_2 ) ) != 0 }; // not all multi-events have memory cell checks, for the ones which don't we can keep quiet about it init_targets( simulation::Memory, "memory cell", conditiontchecksmemcell ); if( m_ignored ) { @@ -1964,6 +1964,22 @@ event_manager::queue( TEventLauncher *Launcher ) { m_launcherqueue.emplace_back( Launcher ); } +// inserts in the event query events assigned to event launchers capable of receiving specified radio message sent from specified location +void +event_manager::queue_receivers( radio_message const Message, glm::dvec3 const &Location ) { + + for( auto *launcher : m_radiodrivenlaunchers.sequence() ) { + if( ( launcher->key() == Message ) + && ( ( launcher->dRadius < 0 ) + || ( glm::length2( launcher->location() - Location ) < launcher->dRadius ) ) + && ( true == launcher->check_conditions() ) ) { + // NOTE: only execution of event1 is supported for radio messages + // TBD, TODO: consider ability/way to execute event2 + simulation::Events.AddToQuery( launcher->Event1, nullptr ); + } + } +} + // legacy method, updates event queues void event_manager::update() { @@ -2187,32 +2203,39 @@ event_manager::InitEvents() { void event_manager::InitLaunchers() { - for( auto *launcher : m_launchers.sequence() ) { + std::vector *> launchertables { + &m_inputdrivenlaunchers, + &m_radiodrivenlaunchers + }; - if( launcher->iCheckMask != 0 ) { - if( launcher->asMemCellName != "none" ) { - // jeśli jest powiązana komórka pamięci - launcher->MemCell = simulation::Memory.find( launcher->asMemCellName ); - if( launcher->MemCell == nullptr ) { - ErrorLog( "Bad scenario: event launcher \"" + launcher->name() + "\" can't find memcell \"" + launcher->asMemCellName + "\"" ); + for( auto *launchertable : launchertables ) { + for( auto *launcher : launchertable->sequence() ) { + + if( launcher->iCheckMask != 0 ) { + if( launcher->asMemCellName != "none" ) { + // jeśli jest powiązana komórka pamięci + launcher->MemCell = simulation::Memory.find( launcher->asMemCellName ); + if( launcher->MemCell == nullptr ) { + ErrorLog( "Bad scenario: event launcher \"" + launcher->name() + "\" can't find memcell \"" + launcher->asMemCellName + "\"" ); + } + } + else { + launcher->MemCell = nullptr; } } - else { - launcher->MemCell = nullptr; - } - } - if( launcher->asEvent1Name != "none" ) { - launcher->Event1 = simulation::Events.FindEvent( launcher->asEvent1Name ); - if( launcher->Event1 == nullptr ) { - ErrorLog( "Bad scenario: event launcher \"" + launcher->name() + "\" can't find event \"" + launcher->asEvent1Name + "\"" ); + if( launcher->asEvent1Name != "none" ) { + launcher->Event1 = simulation::Events.FindEvent( launcher->asEvent1Name ); + if( launcher->Event1 == nullptr ) { + ErrorLog( "Bad scenario: event launcher \"" + launcher->name() + "\" can't find event \"" + launcher->asEvent1Name + "\"" ); + } } - } - if( launcher->asEvent2Name != "none" ) { - launcher->Event2 = simulation::Events.FindEvent( launcher->asEvent2Name ); - if( launcher->Event2 == nullptr ) { - ErrorLog( "Bad scenario: event launcher \"" + launcher->name() + "\" can't find event \"" + launcher->asEvent2Name + "\"" ); + if( launcher->asEvent2Name != "none" ) { + launcher->Event2 = simulation::Events.FindEvent( launcher->asEvent2Name ); + if( launcher->Event2 == nullptr ) { + ErrorLog( "Bad scenario: event launcher \"" + launcher->name() + "\" can't find event \"" + launcher->asEvent2Name + "\"" ); + } } } } @@ -2228,8 +2251,14 @@ event_manager::export_as_text( std::ostream &Output ) const { event->export_as_text( Output ); } } - Output << "// event launchers\n"; - for( auto const *launcher : m_launchers.sequence() ) { + Output << "// event launchers, basic\n"; + for( auto const *launcher : m_inputdrivenlaunchers.sequence() ) { + if( launcher->group() == null_handle ) { + launcher->export_as_text( Output ); + } + } + Output << "// event launchers, radio driven\n"; + for( auto const *launcher : m_radiodrivenlaunchers.sequence() ) { if( launcher->group() == null_handle ) { launcher->export_as_text( Output ); } @@ -2239,7 +2268,7 @@ event_manager::export_as_text( std::ostream &Output ) const { std::vector event_manager::find_eventlaunchers(glm::vec2 center, float radius) const { std::vector results; - for (auto &launcher : m_launchers.sequence()) { + for (auto &launcher : m_inputdrivenlaunchers.sequence()) { glm::dvec3 location = launcher->location(); if (glm::distance2(glm::vec2(location.x, location.z), center) < radius) results.push_back(launcher); diff --git a/Event.h b/Event.h index 01170645..1f402cc6 100644 --- a/Event.h +++ b/Event.h @@ -578,6 +578,9 @@ public: // adds specified event launcher to the list of global launchers void queue( TEventLauncher *Launcher ); + // inserts in the event query events assigned to event launchers capable of receiving specified radio message sent from specified location + void + queue_receivers( radio_message const Message, glm::dvec3 const &Location ); // legacy method, updates event queues void update(); @@ -588,10 +591,13 @@ public: inline bool insert( TEventLauncher *Launcher ) { - return m_launchers.insert( Launcher ); } - inline void purge (TEventLauncher *Launcher) { - m_launchers.purge(Launcher); - } + return ( + Launcher->IsRadioActivated() ? + m_radiodrivenlaunchers.insert( Launcher ) : + m_inputdrivenlaunchers.insert( Launcher ) ); } + inline void purge (TEventLauncher *Launcher) { + m_radiodrivenlaunchers.purge(Launcher); + m_inputdrivenlaunchers.purge(Launcher); } // returns first event in the queue inline basic_event * @@ -607,7 +613,8 @@ public: basic_event * FindEvent( std::string const &Name ); inline TEventLauncher* FindEventlauncher(std::string const &Name) { - return m_launchers.find(Name); + auto ptr = m_inputdrivenlaunchers.find(Name); + return ptr ? ptr : m_radiodrivenlaunchers.find(Name); } // legacy method, inserts specified event in the event query bool @@ -643,7 +650,8 @@ private: basic_event *QueryRootEvent { nullptr }; basic_event *m_workevent { nullptr }; event_map m_eventmap; - basic_table m_launchers; + basic_table m_inputdrivenlaunchers; + basic_table m_radiodrivenlaunchers; eventlauncher_sequence m_launcherqueue; command_relay m_relay; }; diff --git a/Gauge.cpp b/Gauge.cpp index 0ee74581..c135e18f 100644 --- a/Gauge.cpp +++ b/Gauge.cpp @@ -183,6 +183,7 @@ TGauge::Load_mapping( cParser &Input ) { m_type = ( gaugetype == "impulse" ? TGaugeType::push : gaugetype == "return" ? TGaugeType::push : + gaugetype == "delayed" ? TGaugeType::push_delayed : TGaugeType::toggle ); // default } else if( key == "soundinc:" ) { diff --git a/Gauge.h b/Gauge.h index 0a84d5d9..6d36b67c 100644 --- a/Gauge.h +++ b/Gauge.h @@ -23,7 +23,8 @@ enum class TGaugeAnimation { enum class TGaugeType { toggle, - push + push, + push_delayed }; // animowany wskaźnik, mogący przyjmować wiele stanów pośrednich diff --git a/Globals.cpp b/Globals.cpp index 8614aa81..d70be903 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -59,7 +59,7 @@ global_settings::ConfigParse(cParser &Parser) { Parser.getTokens( 1, false ); Parser >> FieldOfView; // guard against incorrect values - FieldOfView = clamp( FieldOfView, 15.0f, 75.0f ); + FieldOfView = clamp( FieldOfView, 10.0f, 75.0f ); } else if (token == "width") { diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index d81bb755..304133f9 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -377,7 +377,7 @@ struct TBrakePressure typedef std::map TBrakePressureTable; /*typy napedow*/ -enum class TEngineType { None, Dumb, WheelsDriven, ElectricSeriesMotor, ElectricInductionMotor, DieselEngine, SteamEngine, DieselElectric }; +enum class TEngineType { None, Dumb, WheelsDriven, ElectricSeriesMotor, ElectricInductionMotor, DieselEngine, SteamEngine, DieselElectric, Main }; /*postac dostarczanej energii*/ enum class TPowerType { NoPower, BioPower, MechPower, ElectricPower, SteamPower }; /*rodzaj paliwa*/ @@ -431,10 +431,22 @@ struct TCurrentCollector { //} }; /*typy źródeł mocy*/ -enum class TPowerSource { NotDefined, InternalSource, Transducer, Generator, Accumulator, CurrentCollector, PowerCable, Heater }; +enum class TPowerSource { NotDefined, InternalSource, Transducer, Generator, Accumulator, CurrentCollector, PowerCable, Heater, Main }; +struct engine_generator { + // ld inputs + double *engine_revolutions; // revs per second of the prime mover + // config + double revolutions_min; // min working revolutions rate, in revs per second + double revolutions_max; // max working revolutions rate, in revs per second + double voltage_min; // voltage generated at min working revolutions + double voltage_max; // voltage generated at max working revolutions + // ld outputs + double revolutions; + double voltage; +}; -struct _mover__1 +struct TAccumulator { double MaxCapacity; TPowerSource RechargeSource; @@ -444,7 +456,7 @@ struct _mover__1 //} }; -struct _mover__2 +struct TPowerCable { TPowerType PowerTrans; double SteamPressure; @@ -454,12 +466,17 @@ struct _mover__2 //} }; -struct _mover__3 +struct THeater { TGrateType Grate; TBoilerType Boiler; }; +struct TTransducer { + // ld inputs + double InputVoltage; +}; + /*parametry źródeł mocy*/ struct TPowerParameters { @@ -471,11 +488,11 @@ struct TPowerParameters { struct { - _mover__3 RHeater; + THeater RHeater; }; struct { - _mover__2 RPowerCable; + TPowerCable RPowerCable; }; struct { @@ -483,15 +500,15 @@ struct TPowerParameters }; struct { - _mover__1 RAccumulator; + TAccumulator RAccumulator; }; struct { - TEngineType GeneratorEngine; + engine_generator EngineGenerator; }; struct { - double InputVoltage; + TTransducer Transducer; }; struct { @@ -633,7 +650,8 @@ enum class TCouplerType { NoCoupler, Articulated, Bare, Chain, Screw, Automatic struct power_coupling { double current{ 0.0 }; double voltage{ 0.0 }; - bool local{ false }; // whether the power comes from external or onboard source + bool is_local{ false }; // whether the power comes from external or onboard source + bool is_live{ false }; // whether the coupling with next vehicle is live }; struct TCoupling { @@ -1037,7 +1055,11 @@ public: /* dizel_auto_min, dizel_auto_max: real; {predkosc obrotowa przelaczania automatycznej skrzyni biegow*/ double dizel_nmax_cutoff = 0.0; /*predkosc obrotowa zadzialania ogranicznika predkosci*/ double dizel_nmin = 0.0; /*najmniejsza dopuszczalna predkosc obrotowa*/ + double dizel_nmin_hdrive = 0.0; /*najmniejsza dopuszczalna predkosc obrotowa w czasie jazdy na hydro */ + double dizel_nmin_hdrive_factor = 0.0; /*wspolczynnik wzrostu obrotow minimalnych hydro zaleznosci od zadanego procentu*/ + double dizel_nmin_retarder = 0.0; /*obroty pracy podczas hamowania retarderem*/ double dizel_minVelfullengage = 0.0; /*najmniejsza predkosc przy jezdzie ze sprzeglem bez poslizgu*/ + double dizel_maxVelANS = 3.0; /*predkosc progowa rozlaczenia przetwornika momentu*/ double dizel_AIM = 1.0; /*moment bezwladnosci walu itp*/ double dizel_engageDia = 0.5; double dizel_engageMaxForce = 6000.0; double dizel_engagefriction = 0.5; /*parametry sprzegla*/ double engagedownspeed = 0.9; @@ -1065,6 +1087,7 @@ public: double hydro_R_FillRateInc = 1.0; /*szybkosc napelniania sprzegla*/ double hydro_R_FillRateDec = 1.0; /*szybkosc oprozniania sprzegla*/ double hydro_R_MinVel = 1.0; /*minimalna predkosc, przy ktorej retarder dziala*/ + double hydro_R_EngageVel = 1.0; /*minimalna predkosc hamowania, przy ktorej sprzeglo jest wciaz wlaczone*/ /*- dla lokomotyw spalinowo-elektrycznych -*/ double AnPos = 0.0; // pozycja sterowania dokladnego (analogowego) bool AnalogCtrl = false; // @@ -1096,10 +1119,10 @@ public: double MED_EPVC_Time = 7; // czas korekcji sily hamowania EP, gdy nie ma dostepnego ED bool MED_Ncor = 0; // czy korekcja sily hamowania z uwzglednieniem nacisku - int DCEMUED_CC; //na którym sprzęgu sprawdzać działanie ED - double DCEMUED_EP_max_Vel; //maksymalna prędkość, przy której działa EP przy włączonym ED w jednostce (dla tocznych) - double DCEMUED_EP_min_Im; //minimalny prąd, przy którym EP nie działa przy włączonym ED w członie (dla silnikowych) - double DCEMUED_EP_delay; //opóźnienie włączenia hamulca EP przy hamowaniu ED - zwłoka wstępna + int DCEMUED_CC { 0 }; //na którym sprzęgu sprawdzać działanie ED + double DCEMUED_EP_max_Vel{ 0.0 }; //maksymalna prędkość, przy której działa EP przy włączonym ED w jednostce (dla tocznych) + double DCEMUED_EP_min_Im{ 0.0 }; //minimalny prąd, przy którym EP nie działa przy włączonym ED w członie (dla silnikowych) + double DCEMUED_EP_delay{ 0.0 }; //opóźnienie włączenia hamulca EP przy hamowaniu ED - zwłoka wstępna /*-dla wagonow*/ struct load_attributes { @@ -1240,6 +1263,8 @@ public: /*-zmienne dla lokomotyw*/ bool Mains = false; /*polozenie glownego wylacznika*/ + double MainsInitTime{ 0.0 }; // config, initialization time (in seconds) of the main circuit after it receives power, before it can be closed + double MainsInitTimeCountdown{ 0.0 }; // current state of main circuit initialization, remaining time (in seconds) until it's ready int MainCtrlPos = 0; /*polozenie glownego nastawnika*/ int ScndCtrlPos = 0; /*polozenie dodatkowego nastawnika*/ int LightsPos = 0; @@ -1305,6 +1330,7 @@ public: double dizel_engagedeltaomega = 0.0; /*roznica predkosci katowych tarcz sprzegla*/ double dizel_n_old = 0.0; /*poredkosc na potrzeby obliczen sprzegiel*/ double dizel_Torque = 0.0; /*poredkosc na potrzeby obliczen sprzegiel*/ + double dizel_nreg_min = 0.0; /*predkosc regulatora minimalna, zmienna w hydro*/ /* - zmienne dla przetowrnika momentu */ double hydro_TC_Fill = 0.0; /*napelnienie*/ @@ -1385,6 +1411,7 @@ public: double fBrakeCtrlPos = -2.0; // płynna nastawa hamulca zespolonego bool bPantKurek3 = true; // kurek trójdrogowy (pantografu): true=połączenie z ZG, false=połączenie z małą sprężarką // domyślnie zbiornik pantografu połączony jest ze zbiornikiem głównym + bool PantAutoValve { false }; // type of installed pantograph compressor valve int iProblem = 0; // flagi problemów z taborem, aby AI nie musiało porównywać; 0=może jechać int iLights[2]; // bity zapalonych świateł tutaj, żeby dało się liczyć pobór prądu @@ -1517,7 +1544,9 @@ public: bool CompressorSwitch( bool State, range_t const Notify = range_t::consist );/*! wl/wyl sprezarki*/ /*-funkcje typowe dla lokomotywy elektrycznej*/ - void ConverterCheck( double const Timestep ); // przetwornica + void MainsCheck( double const Deltatime ); + void PowerCouplersCheck( double const Deltatime ); + void ConverterCheck( double const Timestep ); // przetwornica void HeatingCheck( double const Timestep ); void WaterPumpCheck( double const Timestep ); void WaterHeaterCheck( double const Timestep ); @@ -1557,8 +1586,9 @@ public: bool dizel_EngageSwitch(double state); bool dizel_EngageChange(double dt); bool dizel_AutoGearCheck(void); - double dizel_fillcheck(int mcp); + double dizel_fillcheck(int mcp, double dt); double dizel_Momentum(double dizel_fill, double n, double dt); + double dizel_MomentumRetarder(double n, double dt); // moment hamowania retardera void dizel_HeatSet( float const Value ); void dizel_Heat( double const dt ); bool dizel_StartupCheck(); diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index e0f9752b..1e801352 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -781,7 +781,7 @@ void TMoverParameters::UpdatePantVolume(double dt) // Ra 2014-07: kurek trójdrogowy łączy spr.pom. z pantografami i wyłącznikiem ciśnieniowym WS // Ra 2014-07: zbiornika rozrządu nie pompuje się tu, tylko pantografy; potem można zamknąć // WS i odpalić resztę - if ((TrainType == dt_EZT) ? + if (PantAutoValve ? (PantPress < ScndPipePress) : bPantKurek3) // kurek zamyka połączenie z ZG { // zbiornik pantografu połączony ze zbiornikiem głównym - małą sprężarką się tego nie napompuje @@ -1181,85 +1181,6 @@ double TMoverParameters::ComputeMovement(double dt, double dt1, const TTrackShap const double Vepsilon = 1e-5; const double Aepsilon = 1e-3; // ASBSpeed=0.8; - // T_MoverParameters::ComputeMovement(dt, dt1, Shape, Track, ElectricTraction, NewLoc, NewRot); - // // najpierw kawalek z funkcji w pliku mover.pas - TotalCurrent = 0; - double hvc = - std::max( - std::max( - PantFrontVolt, - PantRearVolt ), - ElectricTraction.TractionVoltage * 0.9 ); - - for( int side = 0; side < 2; ++side ) { - // przekazywanie napiec - auto const oppositeside { ( side == end::front ? end::rear : end::front ) }; - auto const liveconnection{ - ( Couplers[ side ].CouplingFlag & ctrain_power ) - || ( ( Couplers[ side ].CouplingFlag & ctrain_heating ) - && ( Couplers[ side ].Connected->Heating ) ) }; - - if( liveconnection ) { - auto const &connectedcoupler = Couplers[ side ].Connected->Couplers[ Couplers[ side ].ConnectedNr ]; - Couplers[ oppositeside ].power_high.voltage = - std::max( - std::abs( hvc ), - connectedcoupler.power_high.voltage - Couplers[ side ].power_high.current * 0.02 ); - } - else { - Couplers[ oppositeside ].power_high.voltage = std::abs( hvc ) - Couplers[ side ].power_high.current * 0.02; - } - } - - hvc = Couplers[ end::front ].power_high.voltage + Couplers[ end::rear ].power_high.voltage; - - if( std::abs( PantFrontVolt ) + std::abs( PantRearVolt ) < 1.0 ) { - // bez napiecia... - if( hvc != 0.0 ) { - // ...ale jest cos na sprzegach: - // przekazywanie pradow - for( int side = 0; side < 2; ++side ) { - - Couplers[ side ].power_high.local = false; // power, if any, will be from external source - - if( ( Couplers[ side ].CouplingFlag & ctrain_power ) - || ( ( Couplers[ side ].CouplingFlag & ctrain_heating ) - && ( Couplers[ side ].Connected->Heating ) ) ) { - auto const &connectedcoupler = - Couplers[ side ].Connected->Couplers[ - ( Couplers[ side ].ConnectedNr == end::front ? - end::rear : - end::front ) ]; - Couplers[ side ].power_high.current = - connectedcoupler.power_high.current - + Itot * Couplers[ side ].power_high.voltage / hvc; // obciążenie rozkladane stosownie do napiec - } - else { - Couplers[ side ].power_high.current = Itot * Couplers[ side ].power_high.voltage / hvc; - } - } - } - } - else - { - for( int side = 0; side < 2; ++side ) { - - Couplers[ side ].power_high.local = true; // power is coming from local pantographs - - if( ( Couplers[ side ].CouplingFlag & ctrain_power ) - || ( ( Couplers[ side ].CouplingFlag & ctrain_heating ) - && ( Couplers[ side ].Connected->Heating ) ) ) { - auto const &connectedcoupler = - Couplers[ side ].Connected->Couplers[ - ( Couplers[ side ].ConnectedNr == end::front ? - end::rear : - end::front ) ]; - TotalCurrent += connectedcoupler.power_high.current; - Couplers[ side ].power_high.current = 0.0; - } - } - } - if (!TestFlag(DamageFlag, dtrain_out)) { // Ra: to przepisywanie tu jest bez sensu RunningShape = Shape; @@ -1499,10 +1420,11 @@ void TMoverParameters::compute_movement_( double const Deltatime ) { RunInternalCommand(); // automatyczny rozruch - if (EngineType == TEngineType::ElectricSeriesMotor) - - if (AutoRelayCheck()) - SetFlag(SoundFlag, sound::relay); + if( EngineType == TEngineType::ElectricSeriesMotor ) { + if( AutoRelayCheck() ) { + SetFlag( SoundFlag, sound::relay ); + } + } if( ( EngineType == TEngineType::DieselEngine ) || ( EngineType == TEngineType::DieselElectric ) ) { @@ -1510,6 +1432,12 @@ void TMoverParameters::compute_movement_( double const Deltatime ) { SetFlag( SoundFlag, sound::relay ); } } + + // TODO: gather and move current calculations to dedicated method + TotalCurrent = 0; + + // main circuit + MainsCheck( Deltatime ); // traction motors MotorBlowersCheck( Deltatime ); // uklady hamulcowe: @@ -1547,6 +1475,145 @@ void TMoverParameters::compute_movement_( double const Deltatime ) { BrakeSlippingTimer += Deltatime; // automatic doors update_doors( Deltatime ); + + PowerCouplersCheck( Deltatime ); +} + +void TMoverParameters::MainsCheck( double const Deltatime ) { + + // TODO: move other main circuit checks here + + if( MainsInitTime == 0.0 ) { return; } + + if( MainsInitTimeCountdown > 0.0 ) { + MainsInitTimeCountdown -= Deltatime; + } + // TBD, TODO: move voltage calculation to separate method and use also in power coupler state calculation? + auto localvoltage { 0.0 }; + switch( EnginePowerSource.SourceType ) { + case TPowerSource::CurrentCollector: { + localvoltage = + std::max( + localvoltage, + std::max( + PantFrontVolt, + PantRearVolt ) ); + break; + } + default: { + break; + } + } + if( ( localvoltage == 0.0 ) + && ( GetTrainsetVoltage() == 0 ) ) { + MainsInitTimeCountdown = MainsInitTime; + } +} + +void TMoverParameters::PowerCouplersCheck( double const Deltatime ) { + // TODO: add support for other power sources + auto localvoltage { 0.0 }; + // heating power sources + if( Heating ) { + switch( HeatingPowerSource.SourceType ) { + case TPowerSource::Generator: { + localvoltage = HeatingPowerSource.EngineGenerator.voltage - TotalCurrent * 0.02; + break; + } + case TPowerSource::Main: { + localvoltage = ( true == Mains ? Voltage : 0.0 ); + break; + } + default: { + break; + } + } + } + // high voltage power sources + switch( EnginePowerSource.SourceType ) { + case TPowerSource::CurrentCollector: { + localvoltage = + std::max( + localvoltage, + std::max( + PantFrontVolt, + PantRearVolt ) ); + break; + } + default: { + break; + } + } + + auto const abslocalvoltage { std::abs( localvoltage ) }; + auto const localpowersource { ( abslocalvoltage > 1.0 ) }; +/* + auto const localpowersource { ( std::abs( PantFrontVolt ) + std::abs( PantRearVolt ) > 1.0 ) }; + auto hvc = std::max( PantFrontVolt, PantRearVolt ); +*/ + // przekazywanie napiec + for( auto side = 0; side < 2; ++side ) { + + auto &coupler { Couplers[ side ] }; + // NOTE: in the loop we actually update the state of the coupler on the opposite end of the vehicle + auto &oppositecoupler { Couplers[ ( side == end::front ? end::rear : end::front ) ] }; + auto const oppositehighvoltagecoupling { ( oppositecoupler.CouplingFlag & coupling::highvoltage ) != 0 }; + auto const oppositeheatingcoupling { ( oppositecoupler.CouplingFlag & coupling::heating ) != 0 }; + + // start with base voltage + oppositecoupler.power_high.voltage = abslocalvoltage; + oppositecoupler.power_high.is_live = false; + oppositecoupler.power_high.is_local = localpowersource; // indicate power source + // draw from external source + if( coupler.Connected != nullptr ) { + auto const &connectedcoupler { coupler.Connected->Couplers[ coupler.ConnectedNr ] }; + auto const connectedvoltage { ( + connectedcoupler.power_high.is_live ? + connectedcoupler.power_high.voltage : + 0.0 ) }; + oppositecoupler.power_high.voltage = std::max( + oppositecoupler.power_high.voltage, + connectedvoltage - coupler.power_high.current * 0.02 ); + oppositecoupler.power_high.is_live = + ( connectedvoltage > 0.1 ) + && ( oppositehighvoltagecoupling || oppositeheatingcoupling ); + } + // draw from local source + if( localpowersource ) { + oppositecoupler.power_high.voltage = std::max( + oppositecoupler.power_high.voltage, + abslocalvoltage - coupler.power_high.current * 0.02 ); + oppositecoupler.power_high.is_live |= + ( abslocalvoltage > 0.1 ) + && ( oppositehighvoltagecoupling || ( oppositeheatingcoupling && localpowersource && Heating ) ); + } + } + + // przekazywanie pradow + auto couplervoltage { Couplers[ end::front ].power_high.voltage + Couplers[ end::rear ].power_high.voltage }; + + for( auto side = 0; side < 2; ++side ) { + + auto &coupler { Couplers[ side ] }; + auto const &connectedothercoupler { coupler.Connected->Couplers[ ( coupler.ConnectedNr == end::front ? end::rear : end::front ) ] }; + + coupler.power_high.current = 0.0; + if( false == localpowersource ) { + // bez napiecia... + if( couplervoltage != 0.0 ) { + // ...ale jest cos na sprzegach: + coupler.power_high.current = ( Itot + TotalCurrent ) * coupler.power_high.voltage / couplervoltage; // obciążenie rozkladane stosownie do napiec + if( true == coupler.power_high.is_live ) { + coupler.power_high.current += connectedothercoupler.power_high.current; + } + } + } + else { + if( true == coupler.power_high.is_live ) { + TotalCurrent += connectedothercoupler.power_high.current; + } + } + } } double TMoverParameters::ShowEngineRotation(int VehN) @@ -1603,12 +1670,70 @@ void TMoverParameters::ConverterCheck( double const Timestep ) { // heating system status check void TMoverParameters::HeatingCheck( double const Timestep ) { - Heating = ( - ( true == HeatingAllow ) - // powered vehicles are generally required to activate their power source to provide heating - // passive vehicles get a pass in this regard - && ( ( Power < 0.1 ) - || ( true == Mains ) ) ); + // update heating devices + // TBD, TODO: move this to a separate method? + switch( HeatingPowerSource.SourceType ) { + case TPowerSource::Generator: { + if( ( HeatingPowerSource.EngineGenerator.engine_revolutions != nullptr ) + && ( HeatingPowerSource.EngineGenerator.revolutions_max > 0 ) ) { + + auto &generator { HeatingPowerSource.EngineGenerator }; + // TBD, TODO: engine-generator transmission + generator.revolutions = *(generator.engine_revolutions); + + auto const absrevolutions { std::abs( generator.revolutions ) }; + generator.voltage = ( + absrevolutions < generator.revolutions_min ? generator.voltage_min * absrevolutions / generator.revolutions_min : +// absrevolutions > generator.revolutions_max ? generator.voltage_max * absrevolutions / generator.revolutions_max : + interpolate( + generator.voltage_min, generator.voltage_max, + clamp( + ( absrevolutions - generator.revolutions_min ) / ( generator.revolutions_max - generator.revolutions_min ), + 0.0, 1.0 ) ) ) + * sign( generator.revolutions ); + } + break; + } + default: { + break; + } + } + + // quick check first to avoid unnecessary calls... + if( false == HeatingAllow ) { + Heating = false; + return; + } + // ...detailed check if we're still here + auto const heatingpowerthreshold { 0.1 }; + // start with external power sources + auto voltage { 0.0 }; + // then try internal ones + switch( HeatingPowerSource.SourceType ) { + case TPowerSource::Generator: { + voltage = HeatingPowerSource.EngineGenerator.voltage; + break; + } + case TPowerSource::PowerCable: { + if( HeatingPowerSource.PowerType == TPowerType::ElectricPower ) { + voltage = GetTrainsetVoltage(); + } + break; + } + case TPowerSource::Main: { + voltage = ( true == Mains ? Voltage : 0.0 ); + break; + } + default: { + break; + } + } + + Heating = ( std::abs( voltage ) > heatingpowerthreshold ); + + if( Heating ) { + TotalCurrent += 1000 * HeatingPower / voltage; // heater power cost presumably specified in kilowatts + } } // water pump status check @@ -2756,6 +2881,7 @@ void TMoverParameters::MainSwitch_( bool const State ) { if( ( false == State ) || ( ( ( ScndCtrlPos == 0 ) || ( EngineType == TEngineType::ElectricInductionMotor ) ) && ( ( ConvOvldFlag == false ) || ( TrainType == dt_EZT ) ) + && ( MainsInitTimeCountdown <= 0.0 ) && ( true == NoVoltRelay ) && ( true == OvervoltageRelay ) && ( LastSwitchingTime > CtrlDelay ) @@ -3874,7 +4000,6 @@ void TMoverParameters::ComputeConstans(void) double BearingF, RollF, HideModifier; double Curvature; // Ra 2014-07: odwrotność promienia - TotalCurrent = 0; // Ra 2014-04: tu zerowanie, aby EZT mogło pobierać prąd innemu członowi TotalMass = ComputeMass(); TotalMassxg = TotalMass * g; // TotalMass*g BearingF = 2.0 * (DamageFlag && dtrain_bearing); @@ -4013,9 +4138,10 @@ void TMoverParameters::ComputeTotalForce(double dt) { else Voltage = RunningTraction.TractionVoltage * DirAbsolute; // ActiveDir*CabNo; } // bo nie dzialalo + // TODO: clean up this elseif to match changes in power coupling code else if( ( EngineType == TEngineType::ElectricInductionMotor ) || ( ( ( Couplers[ end::front ].CouplingFlag & ctrain_power ) == ctrain_power ) - || ( ( Couplers[ end::rear ].CouplingFlag & ctrain_power ) == ctrain_power ) ) ) { + || ( ( Couplers[ end::rear ].CouplingFlag & ctrain_power ) == ctrain_power ) ) ) { // potem ulepszyc! pantogtrafy! Voltage = std::max( @@ -4405,7 +4531,7 @@ double TMoverParameters::TractionForce( double dt ) { tmp = DElist[ MainCtrlPos ].RPM / 60.0; - if( ( true == Heating ) + if( ( true == HeatingAllow ) && ( HeatingPower > 0 ) && ( EngineHeatingRPM > 0 ) ) { // bump engine revolutions up if needed, when heating is on @@ -4559,7 +4685,7 @@ double TMoverParameters::TractionForce( double dt ) { EnginePower = ( 2 * dizel_Mstand + dmoment ) * enrot * ( 2.0 * M_PI / 1000.0 ); if( MainCtrlPowerPos() > 1 ) { // dodatkowe opory z powodu sprezarki} - dmoment -= dizel_Mstand * ( 0.2 * enrot / dizel_nmax ); +// dmoment -= dizel_Mstand * ( 0.2 * enrot / dizel_nmax ); //yB: skąd to w ogóle się bierze?! } break; } @@ -4742,6 +4868,8 @@ double TMoverParameters::TractionForce( double dt ) { { Mm = dmoment; //bylo * dizel_engage Mw = Mm * dtrans; // dmoment i dtrans policzone przy okazji enginerotation + if ((hydro_R) && (hydro_R_Placement == 0)) + Mw -= dizel_MomentumRetarder(nrot * Transmision.Ratio, dt) * Transmision.Ratio; Fw = Mw * 2.0 / WheelDiameter / NPoweredAxles; Ft = Fw * NPoweredAxles; // sila trakcyjna Ft = Ft * DirAbsolute; // ActiveDir*CabNo; @@ -5282,12 +5410,12 @@ double TMoverParameters::TractionForce( double dt ) { Im = eimv[eimv_If]; if ((eimv[eimv_Ipoj] >= 0)) Vadd *= (1.0 - 2.0 * dt); - else if ((Voltage < EnginePowerSource.CollectorParameters.MaxV)) + else if ((std::abs(Voltage) < EnginePowerSource.CollectorParameters.MaxV)) Vadd *= (1.0 - dt); else Vadd = Max0R( Vadd * (1.0 - 0.2 * dt), - 0.007 * (Voltage - (EnginePowerSource.CollectorParameters.MaxV - 100))); + 0.007 * (std::abs(Voltage) - (EnginePowerSource.CollectorParameters.MaxV - 100))); Itot = eimv[eimv_Ipoj] * (0.01 + Min0R(0.99, 0.99 - Vadd)); EnginePower = abs(eimv[eimv_Ic] * eimv[eimv_U] * NPoweredAxles) / 1000; @@ -6106,9 +6234,34 @@ void TMoverParameters::CheckEIMIC(double dt) } break; case 3: - eimic -= clamp(-UniCtrlList[MainCtrlPos].SetCtrlVal + eimic, 0.0, dt * UniCtrlList[MainCtrlPos].SpeedDown); //odejmuj do X - eimic += clamp(UniCtrlList[MainCtrlPos].SetCtrlVal - eimic, 0.0, dt * UniCtrlList[MainCtrlPos].SpeedUp); //dodawaj do X - eimic = clamp(eimic, UniCtrlList[MainCtrlPos].MinCtrlVal, UniCtrlList[MainCtrlPos].MaxCtrlVal); + if ((UniCtrlList[MainCtrlPos].mode != BrakeCtrlPos) && (MainCtrlActualPos == MainCtrlPos)) //there was no move of controller, but brake only + { + if (BrakeCtrlPos < UniCtrlList[MainCtrlPosNo].mode) + BrakeLevelSet(UniCtrlList[MainCtrlPosNo].mode); //bottom clamping + if (BrakeCtrlPos > UniCtrlList[0].mode) + BrakeLevelSet(UniCtrlList[0].mode); //top clamping + while (BrakeCtrlPos > UniCtrlList[MainCtrlPos].mode) DecMainCtrl(1); //find nearest position + while (BrakeCtrlPos < UniCtrlList[MainCtrlPos].mode) IncMainCtrl(1); //find nearest position + } + else //controller was moved + BrakeLevelSet(UniCtrlList[MainCtrlPos].mode); + + if ((MainCtrlActualPos != MainCtrlPos) || (LastRelayTime>InitialCtrlDelay)) + { + eimic -= clamp(-UniCtrlList[MainCtrlPos].SetCtrlVal + eimic, 0.0, (MainCtrlActualPos == MainCtrlPos ? dt * UniCtrlList[MainCtrlPos].SpeedDown : sign(UniCtrlList[MainCtrlPos].SpeedDown) * 0.01)); //odejmuj do X + eimic += clamp(UniCtrlList[MainCtrlPos].SetCtrlVal - eimic, 0.0, (MainCtrlActualPos == MainCtrlPos ? dt * UniCtrlList[MainCtrlPos].SpeedUp : sign(UniCtrlList[MainCtrlPos].SpeedUp) * 0.01)); //dodawaj do X + eimic = clamp(eimic, UniCtrlList[MainCtrlPos].MinCtrlVal, UniCtrlList[MainCtrlPos].MaxCtrlVal); + } + if (MainCtrlActualPos == MainCtrlPos) + LastRelayTime += dt; + else + { + LastRelayTime = 0; + MainCtrlActualPos = MainCtrlPos; + } + if (Hamulec->GetEDBCP() > 0.3 && eimic < 0) //when braking with pneumatic brake + eimic = 0; //shut off retarder + } auto const eimicpowerenabled { ( ( true == Mains ) || ( Power == 0.0 ) ) @@ -6231,6 +6384,22 @@ bool TMoverParameters::dizel_AutoGearCheck(void) if (Mains) { + if (EIMCtrlType > 0) //sterowanie komputerowe + { + if (dizel_automaticgearstatus == 0) + { + if ((hydro_TC && hydro_TC_Fill > 0.01) || (eimic_real > 0.0)) + dizel_EngageSwitch(1.0); + else + if (Vel > hydro_R_EngageVel && hydro_R && hydro_R_Fill > 0.01) + dizel_EngageSwitch(0.5); + else + dizel_EngageSwitch(0.0); + } + else + dizel_EngageSwitch(0.0); + } + else if (dizel_automaticgearstatus == 0) // ustaw cisnienie w silowniku sprzegla} switch (RList[MainCtrlPos].Mn) { @@ -6362,7 +6531,7 @@ bool TMoverParameters::dizel_Update(double dt) { dizel_EngageChange( dt ); DU = dizel_AutoGearCheck(); double const fillspeed { 2 }; - dizel_fill = dizel_fill + fillspeed * dt * ( dizel_fillcheck( MainCtrlPos ) - dizel_fill ); + dizel_fill = dizel_fill + fillspeed * dt * ( dizel_fillcheck( MainCtrlPos , dt ) - dizel_fill ); } dizel_Heat( dt ); @@ -6374,7 +6543,7 @@ bool TMoverParameters::dizel_Update(double dt) { // Q: 20160715 // oblicza napelnienie, uzwglednia regulator obrotow // ************************************************************************************************* -double TMoverParameters::dizel_fillcheck(int mcp) +double TMoverParameters::dizel_fillcheck(int mcp, double dt) { auto realfill { 0.0 }; @@ -6391,11 +6560,30 @@ double TMoverParameters::dizel_fillcheck(int mcp) } else { // napelnienie zalezne od MainCtrlPos - realfill = RList[ mcp ].R; + if (EIMCtrlType > 0) + { + realfill = std::max(0.0, eimic_real); + if (eimic_real>0 && !hydro_TC_Lockup) + { + dizel_nreg_min = std::min(dizel_nreg_min + 2.5 * dt, dizel_nmin_hdrive + eimic_real * dizel_nmin_hdrive_factor); + } + else + { + if (Vel < hydro_R_EngageVel && hydro_R && hydro_R_Fill > 0.01) + dizel_nreg_min = std::min(dizel_nreg_min + 5.0 * dt, dizel_nmin_retarder); + else + dizel_nreg_min = dizel_nmin; + } + } + else + realfill = RList[mcp].R; } if (dizel_nmax_cutoff > 0) { auto nreg { 0.0 }; + if (EIMCtrlType > 0) + nreg = (eimic_real > 0 ? dizel_nmax : dizel_nmin); + else switch (RList[MainCtrlPos].Mn) { case 0: @@ -6434,7 +6622,7 @@ double TMoverParameters::dizel_fillcheck(int mcp) realfill = 0; if (enrot < nreg) //pod predkoscia regulatora dawka zadana realfill = realfill; - if ((enrot < dizel_nmin * 0.98)&&(RList[mcp].R>0.001)) //jesli ponizej biegu jalowego i niezerowa dawka, to dawaj pelna + if ((enrot < dizel_nreg_min)&&(RList[mcp].R>0.001)) //jesli ponizej biegu jalowego i niezerowa dawka, to dawaj pelna realfill = 1; } } @@ -6460,6 +6648,8 @@ double TMoverParameters::dizel_Momentum(double dizel_fill, double n, double dt) if( enrot > 0 ) { Moment = ( dizel_Mmax - ( dizel_Mmax - dizel_Mnmax ) * square( ( enrot - dizel_nMmax ) / ( dizel_nMmax - dizel_nmax ) ) ) * dizel_fill - dizel_Mstand; + if ((hydro_R) && (hydro_R_Placement == 2)) + Moment -= dizel_MomentumRetarder(enrot, dt); } else { Moment = -dizel_Mstand; @@ -6477,10 +6667,11 @@ double TMoverParameters::dizel_Momentum(double dizel_fill, double n, double dt) if (hydro_TC) //jesli przetwornik momentu { //napelnianie przetwornika - if ((MainCtrlPowerPos() > 0) && (Mains) && (enrot>dizel_nmin*0.9)) + bool IsPower = (EIMCtrlType > 0 ? eimic_real > 0 : MainCtrlPowerPos() > 0); + if ((IsPower) && (Mains) && (enrot>dizel_nmin*0.9)) hydro_TC_Fill += hydro_TC_FillRateInc * dt; //oproznianie przetwornika - if (((IsMainCtrlNoPowerPos()) && (Vel<3)) + if (((!IsPower) && (Vel hydro_TC_LockupSpeed) && (Mains) && (enrot > 0.9 * dizel_nmin) && (MainCtrlPowerPos() > 0)) + if ((Vel > hydro_TC_LockupSpeed) && (Mains) && (enrot > 0.9 * dizel_nmin) && (IsPower)) hydro_TC_LockupRate += hydro_TC_FillRateInc*dt; //luzowanie sprzegla blokujacego - if ((Vel < (MainCtrlPowerPos() > 0 ? hydro_TC_LockupSpeed : hydro_TC_UnlockSpeed)) || (!Mains) || (enrot < 0.8 * dizel_nmin)) + if ((Vel < (IsPower ? hydro_TC_LockupSpeed : hydro_TC_UnlockSpeed)) || (!Mains) || (enrot < 0.8 * dizel_nmin)) hydro_TC_LockupRate -= hydro_TC_FillRateDec*dt; //obcinanie zakresu hydro_TC_LockupRate = clamp(hydro_TC_LockupRate, 0.0, 1.0); @@ -6578,6 +6769,8 @@ double TMoverParameters::dizel_Momentum(double dizel_fill, double n, double dt) double enrot_max = enrot + (Min0R(TorqueC, TorqueL + abs(hydro_TC_TorqueIn)) + Moment) / dizel_AIM * dt; enrot = clamp(n,enrot_min,enrot_max); } + if ((hydro_R) && (hydro_R_Placement == 1)) + gearMoment -= dizel_MomentumRetarder(hydro_TC_nOut, dt); if( ( enrot <= 0 ) && ( false == dizel_spinup ) ) { @@ -6590,6 +6783,40 @@ double TMoverParameters::dizel_Momentum(double dizel_fill, double n, double dt) return gearMoment; } +double TMoverParameters::dizel_MomentumRetarder(double n, double dt) +{ + double RetarderRequest = (Mains ? std::max(0.0, -eimic_real) : 0); + if (Vel < hydro_R_MinVel) + RetarderRequest = 0; + if ((hydro_R_Placement == 2) && (enrot < dizel_nmin)) + { + RetarderRequest = 0; + } + + hydro_R_n = n * 60; + + if (hydro_R_Fill < RetarderRequest) //gdy zadane hamowanie + { + hydro_R_Fill = std::min(hydro_R_Fill + hydro_R_FillRateInc*dt, RetarderRequest); + } + else + { + hydro_R_Fill = std::max(hydro_R_Fill - hydro_R_FillRateDec*dt, RetarderRequest); + } + + double Moment = hydro_R_MaxTorque; + double pwr = Moment * n * M_PI * 2 * 0.001; + if (pwr > hydro_R_MaxPower) + Moment = Moment * hydro_R_MaxPower / pwr; + double moment_in = n*n*hydro_R_TorqueInIn; + Moment = std::min(moment_in, Moment * hydro_R_Fill); + + hydro_R_Torque = Moment; + + return Moment; + +} + // sets component temperatures to specified value void TMoverParameters::dizel_HeatSet( float const Value ) { @@ -6899,8 +7126,8 @@ bool TMoverParameters::ChangeDoorPermitPreset( int const Change, range_t const N Doors.permit_preset = clamp( Doors.permit_preset + Change, 0, Doors.permit_presets.size() - 1 ); auto const doors { Doors.permit_presets[ Doors.permit_preset ] }; - auto const permitleft = doors & 1; - auto const permitright = doors & 2; + auto const permitleft { ( ( doors & 1 ) != 0 ) }; + auto const permitright { ( ( doors & 2 ) != 0 ) }; PermitDoors( ( CabNo > 0 ? side::left : side::right ), permitleft, Notify ); PermitDoors( ( CabNo > 0 ? side::right : side::left ), permitright, Notify ); @@ -7358,15 +7585,11 @@ double TMoverParameters::GetTrainsetVoltage(void) {//ABu: funkcja zwracajaca napiecie dla calego skladu, przydatna dla EZT return std::max( ( ( ( Couplers[end::front].Connected ) - && ( ( Couplers[ end::front ].CouplingFlag & ctrain_power ) - || ( ( Couplers[ end::front ].CouplingFlag & ctrain_heating ) - && ( Couplers[ end::front ].Connected->Heating ) ) ) ) ? + && ( Couplers[ end::front ].Connected->Couplers[ Couplers[ end::front ].ConnectedNr ].power_high.is_live ) ) ? Couplers[end::front].Connected->Couplers[ Couplers[end::front].ConnectedNr ].power_high.voltage : 0.0 ), ( ( ( Couplers[end::rear].Connected ) - && ( ( Couplers[ end::rear ].CouplingFlag & ctrain_power ) - || ( ( Couplers[ end::rear ].CouplingFlag & ctrain_heating ) - && ( Couplers[ end::rear ].Connected->Heating ) ) ) ) ? + && ( Couplers[ end::rear ].Connected->Couplers[ Couplers[ end::rear ].ConnectedNr ].power_high.is_live ) ) ? Couplers[ end::rear ].Connected->Couplers[ Couplers[ end::rear ].ConnectedNr ].power_high.voltage : 0.0 ) ); } @@ -7808,13 +8031,19 @@ bool TMoverParameters::LoadFIZ(std::string chkpath) std::string file = chkpath + TypeName + ".fiz"; WriteLog("LOAD FIZ FROM " + file); - +/* std::ifstream in(file); if (!in.is_open()) { WriteLog("E8 - FIZ FILE NOT EXIST."); return false; } +*/ + cParser fizparser( file, cParser::buffer_FILE ); + if( false == fizparser.ok() ) { + WriteLog( "E8 - FIZ FILE NOT EXIST." ); + return false; + } ConversionError = 0; @@ -7822,8 +8051,13 @@ bool TMoverParameters::LoadFIZ(std::string chkpath) // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ std::unordered_map fizlines; std::string inputline; +/* while (std::getline(in, inputline)) - { +*/ + while( fizparser.ok() ) { + + inputline = fizparser.getToken( false, "\n" ); + bool comment = ( ( inputline.find('#') != std::string::npos ) || ( inputline.compare( 0, 2, "//" ) == 0 ) ); if( true == comment ) { @@ -7857,7 +8091,7 @@ bool TMoverParameters::LoadFIZ(std::string chkpath) startRLIST = false; continue; } - if (issection("END-RL", inputline)) { + if (issection("END-UCL", inputline)) { startBPT = false; startUCLIST = false; continue; @@ -8070,7 +8304,7 @@ bool TMoverParameters::LoadFIZ(std::string chkpath) startBPT = false; fizlines.emplace("UCList", inputline); startUCLIST = true; LISTLINE = 0; - LoadFIZ_RList(inputline); + LoadFIZ_UCList(inputline); continue; } @@ -8124,7 +8358,7 @@ bool TMoverParameters::LoadFIZ(std::string chkpath) continue; } if (true == startUCLIST) { - readRList(inputline); + readUCList(inputline); continue; } if( true == startDLIST ) { @@ -8144,8 +8378,9 @@ bool TMoverParameters::LoadFIZ(std::string chkpath) continue; } } // while line +/* in.close(); - +*/ // Operacje na zebranych parametrach - przypisywanie do wlasciwych zmiennych i ustawianie // zaleznosci @@ -8756,6 +8991,8 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) { false; extract_value(SpeedCtrlAutoTurnOffFlag, "SpeedCtrlATOF", line, ""); + // main circuit + extract_value( MainsInitTime, "MainInitTime", line, "" ); // converter { std::map starts { @@ -8793,6 +9030,9 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) { lookup->second : start_t::manual; } + // pantograph compressor valve + PantAutoValve = ( TrainType == dt_EZT ); // legacy code behaviour, automatic valve was initially installed in all EMUs + extract_value( PantAutoValve, "PantAutoValve", line, "" ); // fuel pump { auto lookup = starts.find( extract_value( "FuelStart", line ) ); @@ -8831,9 +9071,9 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) { void TMoverParameters::LoadFIZ_Blending(std::string const &line) { extract_value(MED_Vmax, "MED_Vmax", line, to_string(Vmax)); - extract_value(MED_Vmin, "MED_Vmin", line, "0"); + extract_value(MED_Vmin, "MED_Vmin", line, ""); extract_value(MED_Vref, "MED_Vref", line, to_string(Vmax)); - extract_value(MED_amax, "MED_amax", line, "9.81"); + extract_value(MED_amax, "MED_amax", line, ""); extract_value(MED_EPVC, "MED_EPVC", line, ""); extract_value(MED_Ncor, "MED_Ncor", line, ""); @@ -8841,10 +9081,10 @@ void TMoverParameters::LoadFIZ_Blending(std::string const &line) { void TMoverParameters::LoadFIZ_DCEMUED(std::string const &line) { - extract_value(DCEMUED_CC, "CouplerCheck", line, "0"); - extract_value(DCEMUED_EP_max_Vel, "EP_max_Vel", line, "0"); - extract_value(DCEMUED_EP_min_Im, "EP_min_Im", line, "0"); - extract_value(DCEMUED_EP_delay, "EP_delay", line, "0"); + extract_value(DCEMUED_CC, "CouplerCheck", line, ""); + extract_value(DCEMUED_EP_max_Vel, "EP_max_Vel", line, ""); + extract_value(DCEMUED_EP_min_Im, "EP_min_Im", line, ""); + extract_value(DCEMUED_EP_delay, "EP_delay", line, ""); } @@ -8873,12 +9113,13 @@ void TMoverParameters::LoadFIZ_Power( std::string const &Line ) { EnginePowerSource.SourceType = LoadFIZ_SourceDecode( extract_value( "EnginePower", Line ) ); LoadFIZ_PowerParamsDecode( EnginePowerSource, "", Line ); - +/* if( ( EnginePowerSource.SourceType == TPowerSource::Generator ) && ( EnginePowerSource.GeneratorEngine == TEngineType::WheelsDriven ) ) { // perpetuum mobile? ConversionError = 666; } +*/ if( Power == 0.0 ) { //jeśli nie ma mocy, np. rozrządcze EZT EnginePowerSource.SourceType = TPowerSource::NotDefined; @@ -8934,6 +9175,19 @@ void TMoverParameters::LoadFIZ_Engine( std::string const &Input ) { extract_value( dizel_nmin, "nmin", Input, "" ); dizel_nmin /= 60.0; + dizel_nreg_min = dizel_nmin * 0.98; + extract_value(dizel_nmin_hdrive, "nmin_hdrive", Input, ""); + dizel_nmin_hdrive /= 60.0; + if (dizel_nmin_hdrive == 0.0) { + dizel_nmin_hdrive = dizel_nmin; + } + extract_value(dizel_nmin_hdrive_factor, "nmin_hdrive_factor", Input, ""); + dizel_nmin_hdrive_factor /= 60.0; + extract_value(dizel_nmin_retarder, "nmin_retarder", Input, ""); + dizel_nmin_retarder /= 60.0; + if (dizel_nmin_retarder == 0.0) { + dizel_nmin_retarder = dizel_nmin; + } // TODO: unify naming scheme and sort out which diesel engine params are used where and how extract_value( nmax, "nmax", Input, "" ); nmax /= 60.0; @@ -8968,6 +9222,7 @@ void TMoverParameters::LoadFIZ_Engine( std::string const &Input ) { extract_value(hydro_TC_TorqueOutOut, "TC_TOO", Input, ""); extract_value(hydro_TC_LockupSpeed, "TC_LS", Input, ""); extract_value(hydro_TC_UnlockSpeed, "TC_ULS", Input, ""); + extract_value(dizel_maxVelANS, "MaxVelANS", Input, ""); extract_value(hydro_R, "IsRetarder", Input, ""); if (true == hydro_R) { @@ -8978,6 +9233,7 @@ void TMoverParameters::LoadFIZ_Engine( std::string const &Input ) { extract_value(hydro_R_FillRateInc, "R_FRI", Input, ""); extract_value(hydro_R_FillRateDec, "R_FRD", Input, ""); extract_value(hydro_R_MinVel, "R_MinVel", Input, ""); + extract_value(hydro_R_EngageVel, "R_EngageVel", Input, ""); } } break; @@ -9201,12 +9457,31 @@ void TMoverParameters::LoadFIZ_PowerParamsDecode( TPowerParameters &Powerparamet } case TPowerSource::Transducer: { - extract_value( Powerparameters.InputVoltage, Prefix + "TransducerInputV", Line, "" ); + extract_value( Powerparameters.Transducer.InputVoltage, Prefix + "TransducerInputV", Line, "" ); break; } case TPowerSource::Generator: { + // prime mover for the generator + auto &generatorparameters { Powerparameters.EngineGenerator }; - Powerparameters.GeneratorEngine = LoadFIZ_EngineDecode( extract_value( Prefix + "GeneratorEngine", Line ) ); + auto const enginetype { LoadFIZ_EngineDecode( extract_value( Prefix + "GeneratorEngine", Line ) ) }; + if( enginetype == TEngineType::Main ) { + generatorparameters.engine_revolutions = &enrot; + } + else { + // TODO: for engine types other than Main create requested engine object and link to its revolutions + generatorparameters.engine_revolutions = nullptr; + generatorparameters.revolutions = 0; + generatorparameters.voltage = 0; + } + // config + extract_value( generatorparameters.voltage_min, Prefix + "GeneratorMinVoltage", Line, "0" ); + extract_value( generatorparameters.voltage_max, Prefix + "GeneratorMaxVoltage", Line, "0" ); + // NOTE: for consistency the fiz file specifies revolutions per minute + extract_value( generatorparameters.revolutions_min, Prefix + "GeneratorMinRPM", Line, "0" ); + extract_value( generatorparameters.revolutions_max, Prefix + "GeneratorMaxRPM", Line, "0" ); + generatorparameters.revolutions_min /= 60; + generatorparameters.revolutions_max /= 60; break; } case TPowerSource::Accumulator: { @@ -9293,7 +9568,8 @@ TPowerSource TMoverParameters::LoadFIZ_SourceDecode( std::string const &Source ) { "CurrentCollector", TPowerSource::CurrentCollector }, { "PowerCable", TPowerSource::PowerCable }, { "Heater", TPowerSource::Heater }, - { "Internal", TPowerSource::InternalSource } + { "Internal", TPowerSource::InternalSource }, + { "Main", TPowerSource::Main } }; auto lookup = powersources.find( Source ); return @@ -9312,7 +9588,8 @@ TEngineType TMoverParameters::LoadFIZ_EngineDecode( std::string const &Engine ) { "Dumb", TEngineType::Dumb }, { "DieselElectric", TEngineType::DieselElectric }, { "DumbDE", TEngineType::DieselElectric }, - { "ElectricInductionMotor", TEngineType::ElectricInductionMotor } + { "ElectricInductionMotor", TEngineType::ElectricInductionMotor }, + { "Main", TEngineType::Main } }; auto lookup = enginetypes.find( Engine ); return @@ -9530,6 +9807,17 @@ bool TMoverParameters::CheckLocomotiveParameters(bool ReadyFlag, int Dir) if( LightsPosNo > 0 ) LightsPos = LightsDefPos; + // NOTE: legacy compatibility behaviour for vehicles without defined heating power source + if( ( EnginePowerSource.SourceType == TPowerSource::CurrentCollector ) + && ( HeatingPowerSource.SourceType == TPowerSource::NotDefined ) ) { + HeatingPowerSource.SourceType = TPowerSource::Main; + } + if( ( HeatingPowerSource.SourceType == TPowerSource::NotDefined ) + && ( HeatingPower > 0 ) ) { + HeatingPowerSource.SourceType = TPowerSource::PowerCable; + HeatingPowerSource.PowerType = TPowerType::ElectricPower; + } + // checking ready flag // to dac potem do init if( ReadyFlag ) // gotowy do drogi diff --git a/MdlMngr.cpp b/MdlMngr.cpp index eca9a15b..9cc9aa83 100644 --- a/MdlMngr.cpp +++ b/MdlMngr.cpp @@ -57,7 +57,7 @@ TModelsManager::LoadModel(std::string const &Name, bool dynamic) { } TModel3d * -TModelsManager::GetModel(std::string const &Name, bool const Dynamic) +TModelsManager::GetModel(std::string const &Name, bool const Dynamic, bool const Logerrors ) { // model może być we wpisie "node...model" albo "node...dynamic", a także być dodatkowym w dynamic // (kabina, wnętrze, ładunek) // dla "node...dynamic" mamy podaną ścieżkę w "\dynamic\" i musi być co najmniej 1 poziom, zwkle @@ -106,7 +106,9 @@ TModelsManager::GetModel(std::string const &Name, bool const Dynamic) } else { // there's nothing matching in the databank nor on the disk, report failure... - ErrorLog( "Bad file: failed do locate 3d model file \"" + filename + "\"", logtype::file ); + if( Logerrors ) { + ErrorLog( "Bad file: failed do locate 3d model file \"" + filename + "\"", logtype::file ); + } // ...and link it with the error model slot m_modelsmap.emplace( filename, null_handle ); } diff --git a/MdlMngr.h b/MdlMngr.h index b70eb30d..196da420 100644 --- a/MdlMngr.h +++ b/MdlMngr.h @@ -22,7 +22,7 @@ private: class TModelsManager { public: // McZapkie: dodalem sciezke, notabene Path!=Patch :) - static TModel3d *GetModel( std::string const &Name, bool dynamic = false ); + static TModel3d *GetModel( std::string const &Name, bool const dynamic = false, bool const Logerrors = true ); private: // types: diff --git a/README.md b/README.md index f7c49037..6a60f43f 100644 --- a/README.md +++ b/README.md @@ -86,16 +86,7 @@ Commands will be written in [`Bash`](https://www.gnu.org/software/bash/). No-Lin - `_d` is debug flag. If you currently have MaSzyna assets, just copy executable to install directory. - Else you must download and unpack assets. - - Links: - - http://eu07.pl/theme/MaSzyna1803.zip - - http://eu07.pl/theme/MaSzynaP1805.zip - - http://eu07.pl/theme/MaSzynaP1806.zip - - http://eu07.pl/theme/MaSzynaP1807.zip - - http://stuff.eu07.pl/MaSzynaP1810.zip - - Override every file. + You can download assets from [eu07.pl](https://eu07.pl/). **Note:** Linux users may have issue, that `Rainsted` (third party starter), may not detect executable. In that case it is possible, to simulate Windows executable, by creation shell script, like this: diff --git a/Traction.cpp b/Traction.cpp index c5f23ab6..1dfa5ed9 100644 --- a/Traction.cpp +++ b/Traction.cpp @@ -529,7 +529,7 @@ glm::vec3 TTraction::wire_color() const { glm::vec3 color; - if( !DebugModeFlag || GfxRenderer.settings.force_normal_traction_render ) + if( !GfxRenderer.settings.traction_debug ) { switch( Material ) { // Ra: kolory podzieliłem przez 2, bo po zmianie ambient za jasne były // trzeba uwzględnić kierunek świecenia Słońca - tylko ze Słońcem widać kolor diff --git a/TractionPower.h b/TractionPower.h index f19a087f..171f2879 100644 --- a/TractionPower.h +++ b/TractionPower.h @@ -15,6 +15,8 @@ http://mozilla.org/MPL/2.0/. class TTractionPowerSource : public scene::basic_node { + friend class debug_panel; + public: // constructor TTractionPowerSource( scene::node_data const &Nodedata ); diff --git a/Train.cpp b/Train.cpp index 3bce6108..514d831b 100644 --- a/Train.cpp +++ b/Train.cpp @@ -17,8 +17,9 @@ http://mozilla.org/MPL/2.0/. #include "Globals.h" #include "simulation.h" -#include "Camera.h" +#include "Event.h" #include "simulationtime.h" +#include "Camera.h" #include "Logs.h" #include "MdlMngr.h" #include "Model3d.h" @@ -340,6 +341,7 @@ TTrain::commandhandler_map const TTrain::m_commandhandlers = { { user_command::radiochanneldecrease, &TTrain::OnCommand_radiochanneldecrease }, { user_command::radiostopsend, &TTrain::OnCommand_radiostopsend }, { user_command::radiostoptest, &TTrain::OnCommand_radiostoptest }, + { user_command::radiocall3send, &TTrain::OnCommand_radiocall3send }, { user_command::cabchangeforward, &TTrain::OnCommand_cabchangeforward }, { user_command::cabchangebackward, &TTrain::OnCommand_cabchangebackward }, { user_command::generictoggle0, &TTrain::OnCommand_generictoggle }, @@ -457,6 +459,8 @@ dictionary_source *TTrain::GetTrainState() { // basic systems state data dict->insert( "battery", mvControlled->Battery ); dict->insert( "linebreaker", mvControlled->Mains ); + dict->insert( "main_init", ( mvControlled->MainsInitTimeCountdown < mvControlled->MainsInitTime ) && ( mvControlled->MainsInitTimeCountdown > 0.0 ) ); + dict->insert( "main_ready", ( false == mvControlled->Mains ) && ( fHVoltage > 0.0 ) && ( mvControlled->MainsInitTimeCountdown <= 0.0 ) ); dict->insert( "converter", mvControlled->ConverterFlag ); dict->insert( "converter_overload", mvControlled->ConvOvldFlag ); dict->insert( "compress", mvControlled->CompressorFlag ); @@ -4713,10 +4717,10 @@ void TTrain::OnCommand_doorcloseall( TTrain *Train, command_data const &Command if( Command.action == GLFW_PRESS ) { if( Train->mvOccupied->Doors.has_autowarning ) { - // automatic departure signal delays actual door closing until the button is released Train->mvOccupied->signal_departure( true ); } - else { + if( Train->ggDoorAllOffButton.type() != TGaugeType::push_delayed ) { + // delays the action until the button is released Train->mvOccupied->OperateDoors( side::right, false ); Train->mvOccupied->OperateDoors( side::left, false ); } @@ -4727,10 +4731,10 @@ void TTrain::OnCommand_doorcloseall( TTrain *Train, command_data const &Command Train->ggDoorAllOffButton.UpdateValue( 1.0, Train->dsbSwitch ); } else if( Command.action == GLFW_RELEASE ) { - // release the button if( Train->mvOccupied->Doors.has_autowarning ) { - // automatic departure signal delays actual door closing until the button is released Train->mvOccupied->signal_departure( false ); + } + if( Train->ggDoorAllOffButton.type() == TGaugeType::push_delayed ) { // now we can actually close the door Train->mvOccupied->OperateDoors( side::right, false ); Train->mvOccupied->OperateDoors( side::left, false ); @@ -5006,6 +5010,7 @@ void TTrain::OnCommand_radiostoptest( TTrain *Train, command_data const &Command if( Command.action == GLFW_PRESS ) { if( ( Train->RadioChannel() == 10 ) + && ( true == Train->mvOccupied->Radio ) && ( Train->mvControlled->Battery || Train->mvControlled->ConverterFlag ) ) { Train->Dynamic()->RadioStop(); } @@ -5018,6 +5023,23 @@ void TTrain::OnCommand_radiostoptest( TTrain *Train, command_data const &Command } } +void TTrain::OnCommand_radiocall3send( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_PRESS ) { + if( ( Train->RadioChannel() != 10 ) + && ( true == Train->mvOccupied->Radio ) + && ( Train->mvControlled->Battery || Train->mvControlled->ConverterFlag ) ) { + simulation::Events.queue_receivers( radio_message::call3, Train->Dynamic()->GetPosition() ); + } + // visual feedback + Train->ggRadioCall3.UpdateValue( 1.0 ); + } + else if( Command.action == GLFW_RELEASE ) { + // visual feedback + Train->ggRadioCall3.UpdateValue( 0.0 ); + } +} + void TTrain::OnCommand_cabchangeforward( TTrain *Train, command_data const &Command ) { if( Command.action == GLFW_PRESS ) { @@ -5032,6 +5054,10 @@ void TTrain::OnCommand_cabchangeforward( TTrain *Train, command_data const &Comm Train->MoveToVehicle(dynobj); } } + // HACK: match consist door permit state with the preset in the active cab + if( Train->ggDoorPermitPresetButton.SubModel != nullptr ) { + Train->mvOccupied->ChangeDoorPermitPreset( 0 ); + } } } @@ -5049,6 +5075,10 @@ void TTrain::OnCommand_cabchangebackward( TTrain *Train, command_data const &Com Train->MoveToVehicle(dynobj); } } + // HACK: match consist door permit state with the preset in the active cab + if( Train->ggDoorPermitPresetButton.SubModel != nullptr ) { + Train->mvOccupied->ChangeDoorPermitPreset( 0 ); + } } } @@ -5127,10 +5157,11 @@ bool TTrain::Update( double const Deltatime ) if( ( ggMainButton.GetDesiredValue() > 0.95 ) || ( ggMainOnButton.GetDesiredValue() > 0.95 ) ) { // keep track of period the line breaker button is held down, to determine when/if circuit closes - if( ( fHVoltage > 0.5 * mvControlled->EnginePowerSource.MaxVoltage ) - || ( ( mvControlled->EngineType != TEngineType::ElectricSeriesMotor ) - && ( mvControlled->EngineType != TEngineType::ElectricInductionMotor ) - && ( true == mvControlled->Battery ) ) ) { + if( ( mvControlled->MainsInitTimeCountdown <= 0.0 ) + && ( ( fHVoltage > 0.5 * mvControlled->EnginePowerSource.MaxVoltage ) + || ( ( mvControlled->EngineType != TEngineType::ElectricSeriesMotor ) + && ( mvControlled->EngineType != TEngineType::ElectricInductionMotor ) + && ( true == mvControlled->Battery ) ) ) ) { // prevent the switch from working if there's no power // TODO: consider whether it makes sense for diesel engines and such fMainRelayTimer += Deltatime; @@ -5640,9 +5671,10 @@ bool TTrain::Update( double const Deltatime ) || (true == mvControlled->Mains) ) ? true : false ) ); - // NOTE: 'off' variant uses the same test, but opposite resulting states btLampkaWylSzybkiOff.Turn( - ( ( ( m_linebreakerstate == 2 ) + ( ( ( mvControlled->MainsInitTimeCountdown > 0.0 ) + || ( fHVoltage == 0.0 ) + || ( m_linebreakerstate == 2 ) || ( true == mvControlled->Mains ) ) ? false : true ) ); @@ -5874,7 +5906,7 @@ bool TTrain::Update( double const Deltatime ) auto const *mover { tmp->MoverParameters }; btLampkaWylSzybkiB.Turn( mover->Mains ); - btLampkaWylSzybkiBOff.Turn( false == mover->Mains ); + btLampkaWylSzybkiBOff.Turn( ( false == mover->Mains ) && ( mover->MainsInitTimeCountdown <= 0.0 ) && ( fHVoltage != 0.0 ) ); btLampkaOporyB.Turn(mover->ResistorsFlagCheck()); btLampkaBezoporowaB.Turn( @@ -6129,6 +6161,7 @@ bool TTrain::Update( double const Deltatime ) ggRadioChannelNext.Update(); ggRadioStop.Update(); ggRadioTest.Update(); + ggRadioCall3.Update(); ggDepartureSignalButton.Update(); ggPantFrontButton.Update(); @@ -7479,6 +7512,7 @@ void TTrain::clear_cab_controls() ggRadioChannelNext.Clear(); ggRadioStop.Clear(); ggRadioTest.Clear(); + ggRadioCall3.Clear(); ggDoorLeftPermitButton.Clear(); ggDoorRightPermitButton.Clear(); ggDoorPermitPresetButton.Clear(); @@ -7796,7 +7830,7 @@ void TTrain::set_cab_controls( int const Cab ) { TimetableLightActive ? 1.f : 0.f ) ); - // doors + // doors permits if( ggDoorLeftPermitButton.type() != TGaugeType::push ) { ggDoorLeftPermitButton.PutValue( mvOccupied->Doors.instances[ ( mvOccupied->ActiveCab == 1 ? side::left : side::right ) ].open_permit ? 1.f : 0.f ); } @@ -7804,6 +7838,7 @@ void TTrain::set_cab_controls( int const Cab ) { ggDoorRightPermitButton.PutValue( mvOccupied->Doors.instances[ ( mvOccupied->ActiveCab == 1 ? side::right : side::left ) ].open_permit ? 1.f : 0.f ); } ggDoorPermitPresetButton.PutValue( mvOccupied->Doors.permit_preset ); + // door controls ggDoorLeftButton.PutValue( mvOccupied->Doors.instances[ ( mvOccupied->ActiveCab == 1 ? side::left : side::right ) ].is_closed ? 0.f : 1.f ); ggDoorRightButton.PutValue( mvOccupied->Doors.instances[ ( mvOccupied->ActiveCab == 1 ? side::right : side::left ) ].is_closed ? 0.f : 1.f ); // door lock @@ -8164,6 +8199,7 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con { "radiochannelnext_sw:", ggRadioChannelNext }, { "radiostop_sw:", ggRadioStop }, { "radiotest_sw:", ggRadioTest }, + { "radiocall3_sw:", ggRadioCall3 }, { "pantfront_sw:", ggPantFrontButton }, { "pantrear_sw:", ggPantRearButton }, { "pantfrontoff_sw:", ggPantFrontButtonOff }, diff --git a/Train.h b/Train.h index 2500a688..1fe2d795 100644 --- a/Train.h +++ b/Train.h @@ -347,6 +347,7 @@ class TTrain static void OnCommand_radiochanneldecrease( TTrain *Train, command_data const &Command ); static void OnCommand_radiostopsend( TTrain *Train, command_data const &Command ); static void OnCommand_radiostoptest( TTrain *Train, command_data const &Command ); + static void OnCommand_radiocall3send( TTrain *Train, command_data const &Command ); static void OnCommand_cabchangeforward( TTrain *Train, command_data const &Command ); static void OnCommand_cabchangebackward( TTrain *Train, command_data const &Command ); static void OnCommand_generictoggle( TTrain *Train, command_data const &Command ); @@ -419,6 +420,7 @@ public: // reszta może by?publiczna TGauge ggRadioChannelNext; TGauge ggRadioTest; TGauge ggRadioStop; + TGauge ggRadioCall3; TGauge ggUpperLightButton; TGauge ggLeftLightButton; TGauge ggRightLightButton; diff --git a/TrkFoll.cpp b/TrkFoll.cpp index 20a5d677..efedd8d1 100644 --- a/TrkFoll.cpp +++ b/TrkFoll.cpp @@ -101,7 +101,7 @@ bool TTrackFollower::Move(double fDistance, bool bPrimary) { // przesuwanie wózka po torach o odległość (fDistance), z wyzwoleniem eventów // bPrimary=true - jest pierwszą osią w pojeździe, czyli generuje eventy i przepisuje pojazd // Ra: zwraca false, jeśli pojazd ma być usunięty - auto const ismoving { ( std::abs( fDistance ) > 0.01 ) && ( Owner->GetVelocity() > 0.01 ) }; + auto const ismoving { /* ( std::abs( fDistance ) > 0.01 ) && */ ( Owner->GetVelocity() > 0.01 ) }; fDistance *= fDirection; // dystans mnożnony przez kierunek double s; // roboczy dystans double dir; // zapamiętany kierunek do sprawdzenia, czy się zmienił diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 50ec575f..566694a8 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -23,7 +23,7 @@ jobs: - task: PublishBuildArtifacts@1 inputs: pathtoPublish: 'build/bin' - artifactName: binaries + artifactName: binaries_linux displayName: 'Publish binaries' - job: macos1013 @@ -43,7 +43,7 @@ jobs: - task: PublishBuildArtifacts@1 inputs: pathtoPublish: 'build/bin' - artifactName: binaries + artifactName: binaries_macos displayName: 'Publish binaries' - job: windows_x64 @@ -64,12 +64,12 @@ jobs: - task: PublishBuildArtifacts@1 inputs: pathtoPublish: 'build/bin' - artifactName: binaries + artifactName: binaries_win64 displayName: 'Publish binaries' - task: PublishBuildArtifacts@1 inputs: pathtoPublish: 'build/pdb' - artifactName: symbols + artifactName: binaries_win64 displayName: 'Publish symbols' - job: windows_x32 @@ -90,10 +90,10 @@ jobs: - task: PublishBuildArtifacts@1 inputs: pathtoPublish: 'build/bin' - artifactName: binaries + artifactName: binaries_win32 displayName: 'Publish binaries' - task: PublishBuildArtifacts@1 inputs: pathtoPublish: 'build/pdb' - artifactName: symbols + artifactName: binaries_win32 displayName: 'Publish symbols' diff --git a/command.cpp b/command.cpp index 05636375..31467da5 100644 --- a/command.cpp +++ b/command.cpp @@ -125,6 +125,7 @@ commanddescription_sequence Commands_descriptions = { { "radiochanneldecrease", command_target::vehicle, command_mode::oneoff }, { "radiostopsend", command_target::vehicle, command_mode::oneoff }, { "radiostoptest", command_target::vehicle, command_mode::oneoff }, + { "radiocall3send", command_target::vehicle, command_mode::oneoff }, // TBD, TODO: make cab change controls entity-centric { "cabchangeforward", command_target::vehicle, command_mode::oneoff }, { "cabchangebackward", command_target::vehicle, command_mode::oneoff }, diff --git a/command.h b/command.h index 3a9e461b..aa01cc05 100644 --- a/command.h +++ b/command.h @@ -119,6 +119,7 @@ enum class user_command { radiochanneldecrease, radiostopsend, radiostoptest, + radiocall3send, cabchangeforward, cabchangebackward, diff --git a/driverkeyboardinput.cpp b/driverkeyboardinput.cpp index 83b7e689..431b52ac 100644 --- a/driverkeyboardinput.cpp +++ b/driverkeyboardinput.cpp @@ -127,6 +127,7 @@ driverkeyboard_input::default_bindings() { { user_command::radiochanneldecrease, GLFW_KEY_R }, { user_command::radiostopsend, GLFW_KEY_PAUSE | keymodifier::shift | keymodifier::control }, { user_command::radiostoptest, GLFW_KEY_R | keymodifier::shift | keymodifier::control }, + { user_command::radiocall3send, GLFW_KEY_BACKSPACE }, { user_command::cabchangeforward, GLFW_KEY_HOME }, { user_command::cabchangebackward, GLFW_KEY_END }, // viewturn, diff --git a/drivermouseinput.cpp b/drivermouseinput.cpp index f20f47ca..cd5d2d7d 100644 --- a/drivermouseinput.cpp +++ b/drivermouseinput.cpp @@ -719,6 +719,9 @@ drivermouse_input::default_bindings() { { "radiotest_sw:", { user_command::radiostoptest, user_command::none } }, + { "radiocall3_sw:", { + user_command::radiocall3send, + user_command::none } }, { "pantfront_sw:", { user_command::pantographtogglefront, user_command::none } }, diff --git a/driveruilayer.cpp b/driveruilayer.cpp index b4cc74b5..c6346060 100644 --- a/driveruilayer.cpp +++ b/driveruilayer.cpp @@ -44,7 +44,7 @@ driver_ui::driver_ui() { m_scenariopanel.size_max = { Global.iWindowWidth * 0.95, Global.iWindowHeight * 0.95 }; m_timetablepanel.title = STR("%-*.*s Time: %d:%02d:%02d"); - m_timetablepanel.size_min = { 435, 110 }; + m_timetablepanel.size_min = { 435, 70}; m_timetablepanel.size_max = { 435, Global.iWindowHeight * 0.95 }; m_transcriptspanel.title = STR("Transcripts"); diff --git a/driveruipanels.cpp b/driveruipanels.cpp index 8c5b1393..45bc60f4 100644 --- a/driveruipanels.cpp +++ b/driveruipanels.cpp @@ -16,6 +16,7 @@ http://mozilla.org/MPL/2.0/. #include "simulationtime.h" #include "Timer.h" #include "Event.h" +#include "TractionPower.h" #include "Camera.h" #include "mtable.h" #include "Train.h" @@ -223,15 +224,15 @@ scenario_panel::render() { void timetable_panel::update() { - if( false == is_open ) { return; } + if( false == is_open ) { return; } - text_lines.clear(); - m_tablelines.clear(); + text_lines.clear(); + m_tablelines.clear(); - auto const *train { simulation::Train }; - auto const *controlled { ( train ? train->Dynamic() : nullptr ) }; - auto const &camera { Global.pCamera }; - auto const &time { simulation::Time.data() }; + auto const *train { simulation::Train }; + auto const *controlled { ( train ? train->Dynamic() : nullptr ) }; + auto const &camera { Global.pCamera }; + auto const &time { simulation::Time.data() }; { // current time std::snprintf( @@ -243,60 +244,60 @@ timetable_panel::update() { time.wMinute, time.wSecond ); - title = m_buffer.data(); - } + title = m_buffer.data(); + } - auto *vehicle { ( - false == FreeFlyModeFlag ? controlled : - camera.m_owner != nullptr ? camera.m_owner : + auto *vehicle { ( + false == FreeFlyModeFlag ? controlled : + camera.m_owner != nullptr ? camera.m_owner : std::get( simulation::Region->find_vehicle( camera.Pos, 20, false, false ) ) ) }; // w trybie latania lokalizujemy wg mapy - if( vehicle == nullptr ) { return; } - // if the nearest located vehicle doesn't have a direct driver, try to query its owner - auto const *owner = ( - ( ( vehicle->Mechanik != nullptr ) && ( vehicle->Mechanik->Primary() ) ) ? - vehicle->Mechanik : - vehicle->ctOwner ); - if( owner == nullptr ) { return; } + if( vehicle == nullptr ) { return; } + // if the nearest located vehicle doesn't have a direct driver, try to query its owner + auto const *owner = ( + ( ( vehicle->Mechanik != nullptr ) && ( vehicle->Mechanik->Primary() ) ) ? + vehicle->Mechanik : + vehicle->ctOwner ); + if( owner == nullptr ) { return; } - auto const *table = owner->TrainTimetable(); - if( table == nullptr ) { return; } + auto const *table = owner->TrainTimetable(); + if( table == nullptr ) { return; } - { // destination - auto textline = Bezogonkow( owner->Relation(), true ); - if( false == textline.empty() ) { - textline += " (" + Bezogonkow( owner->TrainName(), true ) + ")"; - } + // destination + { + auto textline = Bezogonkow( owner->Relation(), true ); + if( false == textline.empty() ) { + textline += " (" + Bezogonkow( owner->TrainName(), true ) + ")"; + } + text_lines.emplace_back( textline, Global.UITextColor ); + } - text_lines.emplace_back( textline, Global.UITextColor ); - } + if( false == is_expanded ) { + // next station + auto const nextstation = owner->NextStop(); + if( false == nextstation.empty() ) { + // jeśli jest podana relacja, to dodajemy punkt następnego zatrzymania + auto textline = " -> " + nextstation; - { // next station - auto const nextstation = owner->NextStop(); - if( false == nextstation.empty() ) { - // jeśli jest podana relacja, to dodajemy punkt następnego zatrzymania - auto textline = " -> " + nextstation; + text_lines.emplace_back( textline, Global.UITextColor ); + } + } - text_lines.emplace_back( textline, Global.UITextColor ); - text_lines.emplace_back( "", Global.UITextColor ); - } - } + if( is_expanded ) { - if( is_expanded ) { - - if( vehicle->MoverParameters->CategoryFlag == 1 ) { - // consist data - auto consistmass { owner->fMass }; - auto consistlength { owner->fLength }; - if( ( owner->mvControlling->TrainType != dt_DMU ) - && ( owner->mvControlling->TrainType != dt_EZT ) ) { + if( vehicle->MoverParameters->CategoryFlag == 1 ) { + // consist data + auto consistmass { owner->fMass }; + auto consistlength { owner->fLength }; + if( ( owner->mvControlling->TrainType != dt_DMU ) + && ( owner->mvControlling->TrainType != dt_EZT ) ) { //odejmij lokomotywy czynne, a przynajmniej aktualną - consistmass -= owner->pVehicle->MoverParameters->TotalMass; - // subtract potential other half of a two-part vehicle + consistmass -= owner->pVehicle->MoverParameters->TotalMass; + // subtract potential other half of a two-part vehicle auto const *previous { owner->pVehicle->Prev( coupling::permanent ) }; - if( previous != nullptr ) { consistmass -= previous->MoverParameters->TotalMass; } + if( previous != nullptr ) { consistmass -= previous->MoverParameters->TotalMass; } auto const *next { owner->pVehicle->Next( coupling::permanent ) }; - if( next != nullptr ) { consistmass -= next->MoverParameters->TotalMass; } + if( next != nullptr ) { consistmass -= next->MoverParameters->TotalMass; } } std::snprintf( m_buffer.data(), m_buffer.size(), @@ -320,82 +321,84 @@ timetable_panel::update() { auto const readycolor { glm::vec4( 84.0f / 255.0f, 164.0f / 255.0f, 132.0f / 255.0f, 1.f ) }; // header - m_tablelines.emplace_back( u8"┌─────┬────────────────────────────────────┬─────────┬─────┐", Global.UITextColor ); + m_tablelines.emplace_back( u8"┌─────┬────────────────────────────────────┬─────────┬─────┐", Global.UITextColor ); - TMTableLine const *tableline; - for( int i = owner->iStationStart; i <= table->StationCount; ++i ) { - // wyświetlenie pozycji z rozkładu - tableline = table->TimeTable + i; // linijka rozkładu + TMTableLine const *tableline; + for( int i = owner->iStationStart; i <= table->StationCount; ++i ) { + // wyświetlenie pozycji z rozkładu + tableline = table->TimeTable + i; // linijka rozkładu - bool vmaxchange { true }; - if( i > owner->iStationStart ) { - auto const *previoustableline { tableline - 1 }; - if( tableline->vmax == previoustableline->vmax ) { - vmaxchange = false; - } - } - std::string vmax { " " }; - if( true == vmaxchange ) { - vmax += to_string( tableline->vmax, 0 ); - vmax = vmax.substr( vmax.size() - 3, 3 ); // z wyrównaniem do prawej - } - auto const station { ( - Bezogonkow( tableline->StationName, true ) - + " " ) - .substr( 0, 34 ) }; - auto const location { ( - ( tableline->km > 0.0 ? - to_string( tableline->km, 2 ) : - "" ) - + " " ) - .substr( 0, 34 - tableline->StationWare.size() ) }; - auto const arrival { ( - tableline->Ah >= 0 ? - to_string( int( 100 + tableline->Ah ) ).substr( 1, 2 ) + ":" + to_minutes_str( tableline->Am, true, 3 ) : - u8" │ " ) }; - auto const departure { ( - tableline->Dh >= 0 ? - to_string( int( 100 + tableline->Dh ) ).substr( 1, 2 ) + ":" + to_minutes_str( tableline->Dm, true, 3 ) : - u8" │ " ) }; - auto const candeparture { ( - ( owner->iStationStart < table->StationIndex ) - && ( i < table->StationIndex ) - && ( ( tableline->Ah < 0 ) // pass-through, always valid - || ( time.wHour * 60 + time.wMinute + time.wSecond * 0.0167 >= tableline->Dh * 60 + tableline->Dm ) ) ) }; - auto const loadchangeinprogress { ( ( static_cast( std::ceil( -1.0 * owner->fStopTime ) ) ) > 0 ) }; - auto const isatpassengerstop { ( true == owner->IsAtPassengerStop ) && ( vehicle->MoverParameters->Vel < 1.0 ) }; - auto const traveltime { ( - i < 2 ? " " : - tableline->Ah >= 0 ? to_minutes_str( CompareTime( table->TimeTable[ i - 1 ].Dh, table->TimeTable[ i - 1 ].Dm, tableline->Ah, tableline->Am ), false, 3 ) : - to_minutes_str( std::max( 0.0, CompareTime( table->TimeTable[ i - 1 ].Dh, table->TimeTable[ i - 1 ].Dm, tableline->Dh, tableline->Dm ) - 0.5 ), false, 3 ) ) }; - auto const linecolor { ( - ( i != owner->iStationStart ) ? Global.UITextColor : - loadchangeinprogress ? loadingcolor : - candeparture ? readycolor : // czas minął i odjazd był, to nazwa stacji będzie na zielono - isatpassengerstop ? waitcolor : - Global.UITextColor ) }; - m_tablelines.emplace_back( - ( u8"│ " + vmax + u8" │ " + station + u8" │ " + arrival + u8" │ " + traveltime + u8" │" ), - linecolor ); - m_tablelines.emplace_back( - ( u8"│ │ " + location + tableline->StationWare + u8" │ " + departure + u8" │ │" ), - linecolor ); - // divider/footer - if( i < table->StationCount ) { - auto const *nexttableline { tableline + 1 }; - if( tableline->vmax == nexttableline->vmax ) { - m_tablelines.emplace_back( u8"│ ├────────────────────────────────────┼─────────┼─────┤", Global.UITextColor ); - } - else { - m_tablelines.emplace_back( u8"├─────┼────────────────────────────────────┼─────────┼─────┤", Global.UITextColor ); - } - } - else { - m_tablelines.emplace_back( u8"└─────┴────────────────────────────────────┴─────────┴─────┘", Global.UITextColor ); - } - } - } - } // is_expanded + bool vmaxchange { true }; + if( i > owner->iStationStart ) { + auto const *previoustableline { tableline - 1 }; + if( tableline->vmax == previoustableline->vmax ) { + vmaxchange = false; + } + } + std::string vmax { " " }; + if( true == vmaxchange ) { + vmax += to_string( tableline->vmax, 0 ); + vmax = vmax.substr( vmax.size() - 3, 3 ); // z wyrównaniem do prawej + } + auto const station { ( + Bezogonkow( tableline->StationName, true ) + + " " ) + .substr( 0, 34 ) }; + auto const location { ( + ( tableline->km > 0.0 ? + to_string( tableline->km, 2 ) : + "" ) + + " " ) + .substr( 0, 34 - tableline->StationWare.size() ) }; + auto const arrival { ( + tableline->Ah >= 0 ? + to_string( int( 100 + tableline->Ah ) ).substr( 1, 2 ) + ":" + to_minutes_str( tableline->Am, true, 3 ) : + u8" │ " ) }; + auto const departure { ( + tableline->Dh >= 0 ? + to_string( int( 100 + tableline->Dh ) ).substr( 1, 2 ) + ":" + to_minutes_str( tableline->Dm, true, 3 ) : + u8" │ " ) }; + auto const candeparture { ( + ( owner->iStationStart < table->StationIndex ) + && ( i < table->StationIndex ) + && ( ( tableline->Ah < 0 ) // pass-through, always valid + || ( time.wHour * 60 + time.wMinute + time.wSecond * 0.0167 >= tableline->Dh * 60 + tableline->Dm ) ) ) }; + auto const loadchangeinprogress { ( ( static_cast( std::ceil( -1.0 * owner->fStopTime ) ) ) > 0 ) }; + auto const isatpassengerstop { ( true == owner->IsAtPassengerStop ) && ( vehicle->MoverParameters->Vel < 1.0 ) }; + auto const traveltime { ( + i < 2 ? " " : + tableline->Ah >= 0 ? to_minutes_str( CompareTime( table->TimeTable[ i - 1 ].Dh, table->TimeTable[ i - 1 ].Dm, tableline->Ah, tableline->Am ), false, 3 ) : + to_minutes_str( std::max( 0.0, CompareTime( table->TimeTable[ i - 1 ].Dh, table->TimeTable[ i - 1 ].Dm, tableline->Dh, tableline->Dm ) - 0.5 ), false, 3 ) ) }; + auto const linecolor { ( + ( i != owner->iStationStart ) ? Global.UITextColor : + loadchangeinprogress ? loadingcolor : + candeparture ? readycolor : // czas minął i odjazd był, to nazwa stacji będzie na zielono + isatpassengerstop ? waitcolor : + Global.UITextColor ) }; + auto const trackcount{ ( tableline->TrackNo == 1 ? u8" ┃ " : u8" ║ " ) }; + m_tablelines.emplace_back( + ( u8"│ " + vmax + u8" │ " + station + trackcount + arrival + u8" │ " + traveltime + u8" │" ), + linecolor ); + m_tablelines.emplace_back( + ( u8"│ │ " + location + tableline->StationWare + trackcount + departure + u8" │ │" ), + linecolor ); + // divider/footer + if( i < table->StationCount ) { + auto const *nexttableline { tableline + 1 }; + std::string const vmaxnext{ ( tableline->vmax == nexttableline->vmax ? u8"│ ├" : u8"├─────┼" ) }; + auto const trackcountnext{ ( nexttableline->TrackNo == 1 ? u8"╂" : u8"╫" ) }; + m_tablelines.emplace_back( + vmaxnext + u8"────────────────────────────────────" + trackcountnext + u8"─────────┼─────┤", + Global.UITextColor ); + } + else { + m_tablelines.emplace_back( + u8"└─────┴────────────────────────────────────┴─────────┴─────┘", + Global.UITextColor ); + } + } + } + } // is_expanded } void @@ -438,49 +441,51 @@ timetable_panel::render() { void debug_panel::update() { - if( false == is_open ) { return; } + if( false == is_open ) { return; } - // input item bindings - m_input.train = simulation::Train; - m_input.controlled = ( m_input.train ? m_input.train->Dynamic() : nullptr ); - m_input.camera = &( Global.pCamera ); - m_input.vehicle = ( - false == FreeFlyModeFlag ? m_input.controlled : - m_input.camera->m_owner != nullptr ? m_input.camera->m_owner : - std::get( simulation::Region->find_vehicle( m_input.camera->Pos, 20, false, false ) ) ); // w trybie latania lokalizujemy wg mapy - m_input.mover = - ( m_input.vehicle != nullptr ? - m_input.vehicle->MoverParameters : - nullptr ); - m_input.mechanik = ( - m_input.vehicle != nullptr ? - m_input.vehicle->Mechanik : - nullptr ); + // input item bindings + m_input.train = simulation::Train; + m_input.controlled = ( m_input.train ? m_input.train->Dynamic() : nullptr ); + m_input.camera = &( Global.pCamera ); + m_input.vehicle = ( + false == FreeFlyModeFlag ? m_input.controlled : + m_input.camera->m_owner != nullptr ? m_input.camera->m_owner : + std::get( simulation::Region->find_vehicle( m_input.camera->Pos, 20, false, false ) ) ); // w trybie latania lokalizujemy wg mapy + m_input.mover = + ( m_input.vehicle != nullptr ? + m_input.vehicle->MoverParameters : + nullptr ); + m_input.mechanik = ( + m_input.vehicle != nullptr ? + m_input.vehicle->Mechanik : + nullptr ); - // header section - text_lines.clear(); + // header section + text_lines.clear(); - auto textline = "Version " + Global.asVersion; + auto textline = "Version " + Global.asVersion; - text_lines.emplace_back( textline, Global.UITextColor ); + text_lines.emplace_back( textline, Global.UITextColor ); - // sub-sections - m_vehiclelines.clear(); - m_enginelines.clear(); - m_ailines.clear(); - m_scantablelines.clear(); - m_scenariolines.clear(); - m_eventqueuelines.clear(); - m_cameralines.clear(); + // sub-sections + m_vehiclelines.clear(); + m_enginelines.clear(); + m_ailines.clear(); + m_scantablelines.clear(); + m_scenariolines.clear(); + m_eventqueuelines.clear(); + m_powergridlines.clear(); + m_cameralines.clear(); m_rendererlines.clear(); - update_section_vehicle( m_vehiclelines ); - update_section_engine( m_enginelines ); - update_section_ai( m_ailines ); - update_section_scantable( m_scantablelines ); - update_section_scenario( m_scenariolines ); - update_section_eventqueue( m_eventqueuelines ); - update_section_camera( m_cameralines ); + update_section_vehicle( m_vehiclelines ); + update_section_engine( m_enginelines ); + update_section_ai( m_ailines ); + update_section_scantable( m_scantablelines ); + update_section_scenario( m_scenariolines ); + update_section_eventqueue( m_eventqueuelines ); + update_section_powergrid( m_powergridlines ); + update_section_camera( m_cameralines ); update_section_renderer( m_rendererlines ); } @@ -518,24 +523,22 @@ debug_panel::render() { render_section( "Vehicle Scan Table", m_scantablelines ); render_section( "Scenario", m_scenariolines ); if( true == render_section( "Scenario Event Queue", m_eventqueuelines ) ) { - // event queue filter - ImGui::Checkbox( "By This Vehicle Only", &m_eventqueueactivevehicleonly ); - } + // event queue filter + ImGui::Checkbox( "By This Vehicle Only", &m_eventqueueactivevehicleonly ); + } + if( true == render_section( "Power Grid", m_powergridlines ) ) { + // traction state debug + ImGui::Checkbox( + "Traction debug", + &GfxRenderer.settings.traction_debug ); + } render_section( "Camera", m_cameralines ); render_section( "Gfx Renderer", m_rendererlines ); - // toggles - ImGui::Separator(); - ImGui::Checkbox( "Debug Mode", &DebugModeFlag ); - if( DebugModeFlag ) - { - ImGui::Indent(); - ImGui::Checkbox( - "Draw normal traction", - &GfxRenderer.settings.force_normal_traction_render ); - ImGui::Unindent(); - } - } - ImGui::End(); + // toggles + ImGui::Separator(); + ImGui::Checkbox( "Debug Mode", &DebugModeFlag ); + } + ImGui::End(); } void @@ -569,7 +572,7 @@ debug_panel::update_section_vehicle( std::vector &Output ) { std::snprintf( m_buffer.data(), m_buffer.size(), - STR_C("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"), + STR_C("Devices: %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%s%s\nPower transfers: %.0f@%.0f%s%s[%.0f]%s%s%.0f@%.0f"), // devices ( mover.Battery ? 'B' : '.' ), ( mover.Mains ? 'M' : '.' ), @@ -585,14 +588,17 @@ debug_panel::update_section_vehicle( std::vector &Output ) { ( mover.ConvOvldFlag ? '!' : '.' ), ( mover.CompressorFlag ? 'C' : ( false == mover.CompressorAllowLocal ? '-' : ( ( mover.CompressorAllow || mover.CompressorStart == start_t::automatic ) ? 'c' : '.' ) ) ), ( mover.CompressorGovernorLock ? '!' : '.' ), + ( mover.Heating ? 'H' : ( mover.HeatingAllow ? 'h' : '.' ) ), std::string( isplayervehicle ? STR(" radio: ") + ( mover.Radio ? std::to_string( m_input.train->RadioChannel() ) : "-" ) : "" ).c_str(), std::string( isdieselenginepowered ? STR(" oil pressure: ") + to_string( mover.OilPump.pressure, 2 ) : "" ).c_str(), // power transfers mover.Couplers[ end::front ].power_high.voltage, mover.Couplers[ end::front ].power_high.current, - std::string( mover.Couplers[ end::front ].power_high.local ? "" : "-" ).c_str(), + std::string( mover.Couplers[ end::front ].power_high.is_local ? "" : "-" ).c_str(), std::string( vehicle.DirectionGet() ? ":<<:" : ":>>:" ).c_str(), - std::string( mover.Couplers[ end::rear ].power_high.local ? "" : "-" ).c_str(), + mover.Voltage, + std::string( vehicle.DirectionGet() ? "<<:" : ">>:" ).c_str(), + std::string( mover.Couplers[ end::rear ].power_high.is_local ? "" : "-" ).c_str(), mover.Couplers[ end::rear ].power_high.voltage, mover.Couplers[ end::rear ].power_high.current ); @@ -704,369 +710,416 @@ debug_panel::update_vehicle_coupler( int const Side ) { // NOTE: mover and vehicle are guaranteed to be valid by the caller std::string couplerstatus { STR("none") }; - auto const *connected { m_input.vehicle->MoverParameters->Neighbours[ Side ].vehicle }; + auto const *connected { m_input.vehicle->MoverParameters->Neighbours[ Side ].vehicle }; - if( connected == nullptr ) { return couplerstatus; } + if( connected == nullptr ) { return couplerstatus; } - auto const &mover { *( m_input.mover ) }; + auto const &mover { *( m_input.mover ) }; - std::snprintf( - m_buffer.data(), m_buffer.size(), - "%s [%d] (%.1f m)", - connected->name().c_str(), - mover.Couplers[ Side ].CouplingFlag, - mover.Neighbours[ Side ].distance ); + std::snprintf( + m_buffer.data(), m_buffer.size(), + "%s [%d] (%.1f m)", + connected->name().c_str(), + mover.Couplers[ Side ].CouplingFlag, + mover.Neighbours[ Side ].distance ); - return { m_buffer.data() }; + return { m_buffer.data() }; } std::string debug_panel::update_vehicle_brake() const { - // NOTE: mover is guaranteed to be valid by the caller - auto const &mover { *( m_input.mover ) }; + // NOTE: mover is guaranteed to be valid by the caller + auto const &mover { *( m_input.mover ) }; - std::string brakedelay; + std::string brakedelay; - std::vector> delays { - { bdelay_G, "G" }, - { bdelay_P, "P" }, - { bdelay_R, "R" }, - { bdelay_M, "+Mg" } }; + std::vector> delays { + { bdelay_G, "G" }, + { bdelay_P, "P" }, + { bdelay_R, "R" }, + { bdelay_M, "+Mg" } }; - for( auto const &delay : delays ) { - if( ( mover.BrakeDelayFlag & delay.first ) == delay.first ) { - brakedelay += delay.second; - } - } + for( auto const &delay : delays ) { + if( ( mover.BrakeDelayFlag & delay.first ) == delay.first ) { + brakedelay += delay.second; + } + } - return brakedelay; + return brakedelay; } void debug_panel::update_section_engine( std::vector &Output ) { - if( m_input.train == nullptr ) { return; } - if( m_input.vehicle == nullptr ) { return; } - if( m_input.mover == nullptr ) { return; } + if( m_input.train == nullptr ) { return; } + if( m_input.vehicle == nullptr ) { return; } + if( m_input.mover == nullptr ) { return; } - auto const &train { *m_input.train }; - auto const &vehicle{ *m_input.vehicle }; - auto const &mover{ *m_input.mover }; + auto const &train { *m_input.train }; + auto const &vehicle{ *m_input.vehicle }; + auto const &mover{ *m_input.mover }; - // engine data - // induction motor data - if( mover.EngineType == TEngineType::ElectricInductionMotor ) { + // engine data + // induction motor data + if( mover.EngineType == TEngineType::ElectricInductionMotor ) { - Output.emplace_back( " eimc: eimv: press:", Global.UITextColor ); - for( int i = 0; i <= 20; ++i ) { + Output.emplace_back( " eimc: eimv: press:", Global.UITextColor ); + for( int i = 0; i <= 20; ++i ) { - std::string parameters = - mover.eimc_labels[ i ] + to_string( mover.eimc[ i ], 2, 9 ) - + " | " - + mover.eimv_labels[ i ] + to_string( mover.eimv[ i ], 2, 9 ); + std::string parameters = + mover.eimc_labels[ i ] + to_string( mover.eimc[ i ], 2, 9 ) + + " | " + + mover.eimv_labels[ i ] + to_string( mover.eimv[ i ], 2, 9 ); - if( i < 10 ) { - parameters += " | " + train.fPress_labels[ i ] + to_string( train.fPress[ i ][ 0 ], 2, 9 ); - } - else if( i == 12 ) { - parameters += " med:"; - } - else if( i >= 13 ) { - parameters += " | " + vehicle.MED_labels[ i - 13 ] + to_string( vehicle.MED[ 0 ][ i - 13 ], 2, 9 ); - } + if( i < 10 ) { + parameters += " | " + train.fPress_labels[ i ] + to_string( train.fPress[ i ][ 0 ], 2, 9 ); + } + else if( i == 12 ) { + parameters += " med:"; + } + else if( i >= 13 ) { + parameters += " | " + vehicle.MED_labels[ i - 13 ] + to_string( vehicle.MED[ 0 ][ i - 13 ], 2, 9 ); + } - Output.emplace_back( parameters, Global.UITextColor ); - } - } - if( mover.EngineType == TEngineType::DieselEngine ) { + Output.emplace_back( parameters, Global.UITextColor ); + } + } + if( mover.EngineType == TEngineType::DieselEngine ) { - std::string parameterstext = "param value"; - std::vector< std::pair > const paramvalues { + std::string parameterstext = "param value"; + std::vector< std::pair > const paramvalues { { " rpm: ", mover.enrot * 60.0 }, { "efill: ", mover.dizel_fill }, - { "etorq: ", mover.dizel_Torque }, - { "creal: ", mover.dizel_engage }, - { "cdesi: ", mover.dizel_engagestate }, - { "cdelt: ", mover.dizel_engagedeltaomega }, - { "gears: ", mover.dizel_automaticgearstatus} }; - for( auto const ¶meter : paramvalues ) { - parameterstext += "\n" + parameter.first + to_string( parameter.second, 2, 9 ); - } - Output.emplace_back( parameterstext, Global.UITextColor ); + { "etorq: ", mover.dizel_Torque }, + { "creal: ", mover.dizel_engage }, + { "cdesi: ", mover.dizel_engagestate }, + { "cdelt: ", mover.dizel_engagedeltaomega }, + { "gears: ", mover.dizel_automaticgearstatus} }; + for( auto const ¶meter : paramvalues ) { + parameterstext += "\n" + parameter.first + to_string( parameter.second, 2, 9 ); + } + Output.emplace_back( parameterstext, Global.UITextColor ); - parameterstext = "hydro value"; - std::vector< std::pair > const hydrovalues { - { "hTCnI: ", mover.hydro_TC_nIn }, - { "hTCnO: ", mover.hydro_TC_nOut }, - { "hTCTM: ", mover.hydro_TC_TMRatio }, - { "hTCTI: ", mover.hydro_TC_TorqueIn }, - { "hTCTO: ", mover.hydro_TC_TorqueOut }, - { "hTCfl: ", mover.hydro_TC_Fill }, - { "hTCLR: ", mover.hydro_TC_LockupRate } }; - for( auto const ¶meter : hydrovalues ) { - parameterstext += "\n" + parameter.first + to_string( parameter.second, 2, 9 ); - } - Output.emplace_back( parameterstext, Global.UITextColor ); - } + parameterstext = "hydro value"; + std::vector< std::pair > const hydrovalues { + { "hTCnI: ", mover.hydro_TC_nIn }, + { "hTCnO: ", mover.hydro_TC_nOut }, + { "hTCTM: ", mover.hydro_TC_TMRatio }, + { "hTCTI: ", mover.hydro_TC_TorqueIn }, + { "hTCTO: ", mover.hydro_TC_TorqueOut }, + { "hTCfl: ", mover.hydro_TC_Fill }, + { "hRtFl: ", mover.hydro_R_Fill } , + { " hRtn: ", mover.hydro_R_n } , + { "hRtTq: ", mover.hydro_R_Torque } + + }; + for( auto const ¶meter : hydrovalues ) { + parameterstext += "\n" + parameter.first + to_string( parameter.second, 2, 9 ); + } + Output.emplace_back( parameterstext, Global.UITextColor ); + } } void debug_panel::update_section_ai( std::vector &Output ) { - if( m_input.mover == nullptr ) { return; } - if( m_input.mechanik == nullptr ) { return; } + if( m_input.mover == nullptr ) { return; } + if( m_input.mechanik == nullptr ) { return; } - auto const &mover{ *m_input.mover }; - auto const &mechanik{ *m_input.mechanik }; + auto const &mover{ *m_input.mover }; + auto const &mechanik{ *m_input.mechanik }; - // biezaca komenda dla AI - auto textline = - "Current order: [" + std::to_string( mechanik.OrderPos ) + "] " - + mechanik.OrderCurrent(); + // biezaca komenda dla AI + auto textline = + "Current order: [" + std::to_string( mechanik.OrderPos ) + "] " + + mechanik.OrderCurrent(); - if( mechanik.fStopTime < 0.0 ) { - textline += "\n stop time: " + to_string( std::abs( mechanik.fStopTime ), 1 ); - } + if( mechanik.fStopTime < 0.0 ) { + textline += "\n stop time: " + to_string( std::abs( mechanik.fStopTime ), 1 ); + } - Output.emplace_back( textline, Global.UITextColor ); + Output.emplace_back( textline, Global.UITextColor ); - if( ( mechanik.VelNext == 0.0 ) - && ( mechanik.eSignNext ) ) { - // jeśli ma zapamiętany event semafora, nazwa eventu semafora - Output.emplace_back( "Current signal: " + Bezogonkow( mechanik.eSignNext->m_name ), Global.UITextColor ); - } + if( ( mechanik.VelNext == 0.0 ) + && ( mechanik.eSignNext ) ) { + // jeśli ma zapamiętany event semafora, nazwa eventu semafora + Output.emplace_back( "Current signal: " + Bezogonkow( mechanik.eSignNext->m_name ), Global.UITextColor ); + } - // distances - textline = - "Distances:\n proximity: " + to_string( mechanik.ActualProximityDist, 0 ) - + ", braking: " + to_string( mechanik.fBrakeDist, 0 ); + // distances + textline = + "Distances:\n proximity: " + to_string( mechanik.ActualProximityDist, 0 ) + + ", braking: " + to_string( mechanik.fBrakeDist, 0 ); - if( mechanik.Obstacle.distance < 5000 ) { - textline += - "\n obstacle: " + to_string( mechanik.Obstacle.distance, 0 ) - + " (" + mechanik.Obstacle.vehicle->asName + ")"; - } + if( mechanik.Obstacle.distance < 5000 ) { + textline += + "\n obstacle: " + to_string( mechanik.Obstacle.distance, 0 ) + + " (" + mechanik.Obstacle.vehicle->asName + ")"; + } - Output.emplace_back( textline, Global.UITextColor ); + Output.emplace_back( textline, Global.UITextColor ); - // velocity factors - textline = - "Velocity:\n desired: " + to_string( mechanik.VelDesired, 0 ) - + ", next: " + to_string( mechanik.VelNext, 0 ); + // velocity factors + textline = + "Velocity:\n desired: " + to_string( mechanik.VelDesired, 0 ) + + ", next: " + to_string( mechanik.VelNext, 0 ); - std::vector< std::pair< double, std::string > > const restrictions{ - { mechanik.VelSignalLast, "signal" }, - { mechanik.VelLimitLast, "limit" }, - { mechanik.VelRoad, "road" }, - { mechanik.VelRestricted, "restricted" }, - { mover.RunningTrack.Velmax, "track" } }; + std::vector< std::pair< double, std::string > > const restrictions{ + { mechanik.VelSignalLast, "signal" }, + { mechanik.VelLimitLast, "limit" }, + { mechanik.VelRoad, "road" }, + { mechanik.VelRestricted, "restricted" }, + { mover.RunningTrack.Velmax, "track" } }; - std::string restrictionstext; - for( auto const &restriction : restrictions ) { - if( restriction.first < 0.0 ) { continue; } - if( false == restrictionstext.empty() ) { - restrictionstext += ", "; - } - restrictionstext += - to_string( restriction.first, 0 ) - + " (" + restriction.second + ")"; - } + std::string restrictionstext; + for( auto const &restriction : restrictions ) { + if( restriction.first < 0.0 ) { continue; } + if( false == restrictionstext.empty() ) { + restrictionstext += ", "; + } + restrictionstext += + to_string( restriction.first, 0 ) + + " (" + restriction.second + ")"; + } - if( false == restrictionstext.empty() ) { - textline += "\n restrictions: " + restrictionstext; - } + if( false == restrictionstext.empty() ) { + textline += "\n restrictions: " + restrictionstext; + } - Output.emplace_back( textline, Global.UITextColor ); + Output.emplace_back( textline, Global.UITextColor ); - // acceleration - textline = - "Acceleration:\n desired: " + to_string( mechanik.AccDesired, 2 ) - + ", corrected: " + to_string( mechanik.AccDesired * mechanik.BrakeAccFactor(), 2 ) - + "\n current: " + to_string( mechanik.AbsAccS_pub + 0.001f, 2 ) - + ", slope: " + to_string( mechanik.fAccGravity + 0.001f, 2 ) + " (" + ( mechanik.fAccGravity > 0.01 ? "\\" : ( mechanik.fAccGravity < -0.01 ? "/" : "-" ) ) + ")" - + "\n brake threshold: " + to_string( mechanik.fAccThreshold, 2 ) - + ", delays: " + to_string( mechanik.fBrake_a0[ 0 ], 2 ) - + "+" + to_string( mechanik.fBrake_a1[ 0 ], 2 ) - + "\n virtual brake position: " + to_string(mechanik.BrakeCtrlPosition, 2); + // acceleration + textline = + "Acceleration:\n desired: " + to_string( mechanik.AccDesired, 2 ) + + ", corrected: " + to_string( mechanik.AccDesired * mechanik.BrakeAccFactor(), 2 ) + + "\n current: " + to_string( mechanik.AbsAccS_pub + 0.001f, 2 ) + + ", slope: " + to_string( mechanik.fAccGravity + 0.001f, 2 ) + " (" + ( mechanik.fAccGravity > 0.01 ? "\\" : ( mechanik.fAccGravity < -0.01 ? "/" : "-" ) ) + ")" + + "\n brake threshold: " + to_string( mechanik.fAccThreshold, 2 ) + + ", delays: " + to_string( mechanik.fBrake_a0[ 0 ], 2 ) + + "+" + to_string( mechanik.fBrake_a1[ 0 ], 2 ) + + "\n virtual brake position: " + to_string(mechanik.BrakeCtrlPosition, 2) + + "\n desired diesel percentage: " + to_string(mechanik.DizelPercentage, 0) + + "/" + to_string(mechanik.DizelPercentage_Speed, 0) + + "/" + to_string(100.4*mechanik.mvControlling->eimic_real, 0); - Output.emplace_back( textline, Global.UITextColor ); + Output.emplace_back( textline, Global.UITextColor ); - // brakes - textline = - "Brakes:\n consist: " + to_string( mechanik.fReady, 2 ) + " or less"; + // brakes + textline = + "Brakes:\n consist: " + to_string( mechanik.fReady, 2 ) + " or less"; - Output.emplace_back( textline, Global.UITextColor ); + Output.emplace_back( textline, Global.UITextColor ); - // ai driving flags - std::vector const drivingflagnames { - "StopCloser", "StopPoint", "Active", "Press", "Connect", "Primary", "Late", "StopHere", - "StartHorn", "StartHornNow", "StartHornDone", "Oerlikons", "IncSpeed", "TrackEnd", "SwitchFound", "GuardSignal", - "Visibility", "DoorOpened", "PushPull", "SemaphorFound", "StopPointFound" /*"SemaphorWasElapsed", "TrainInsideStation", "SpeedLimitFound"*/ }; + // ai driving flags + std::vector const drivingflagnames { + "StopCloser", "StopPoint", "Active", "Press", "Connect", "Primary", "Late", "StopHere", + "StartHorn", "StartHornNow", "StartHornDone", "Oerlikons", "IncSpeed", "TrackEnd", "SwitchFound", "GuardSignal", + "Visibility", "DoorOpened", "PushPull", "SemaphorFound", "StopPointFound" /*"SemaphorWasElapsed", "TrainInsideStation", "SpeedLimitFound"*/ }; - textline = "Driving flags:"; - for( int idx = 0, flagbit = 1; idx < drivingflagnames.size(); ++idx, flagbit <<= 1 ) { - if( mechanik.DrivigFlags() & flagbit ) { - textline += "\n " + drivingflagnames[ idx ]; - } - } + textline = "Driving flags:"; + for( int idx = 0, flagbit = 1; idx < drivingflagnames.size(); ++idx, flagbit <<= 1 ) { + if( mechanik.DrivigFlags() & flagbit ) { + textline += "\n " + drivingflagnames[ idx ]; + } + } - Output.emplace_back( textline, Global.UITextColor ); + Output.emplace_back( textline, Global.UITextColor ); } void debug_panel::update_section_scantable( std::vector &Output ) { - if( m_input.mechanik == nullptr ) { return; } + if( m_input.mechanik == nullptr ) { return; } - Output.emplace_back( "Flags: Dist: Vel: Name:", Global.UITextColor ); + Output.emplace_back( "Flags: Dist: Vel: Name:", Global.UITextColor ); - auto const &mechanik{ *m_input.mechanik }; + auto const &mechanik{ *m_input.mechanik }; - std::size_t i = 0; std::size_t const speedtablesize = clamp( static_cast( mechanik.TableSize() ) - 1, 0, 30 ); - do { - auto const scanline = mechanik.TableText( i ); - if( scanline.empty() ) { break; } - Output.emplace_back( Bezogonkow( scanline ), Global.UITextColor ); - ++i; - } while( i < speedtablesize ); - if( Output.size() == 1 ) { - Output.front().data = "(no points of interest)"; - } + std::size_t i = 0; std::size_t const speedtablesize = clamp( static_cast( mechanik.TableSize() ) - 1, 0, 30 ); + do { + auto const scanline = mechanik.TableText( i ); + if( scanline.empty() ) { break; } + Output.emplace_back( Bezogonkow( scanline ), Global.UITextColor ); + ++i; + } while( i < speedtablesize ); + if( Output.size() == 1 ) { + Output.front().data = "(no points of interest)"; + } } void debug_panel::update_section_scenario( std::vector &Output ) { - auto textline = - "vehicles: " + to_string( Timer::subsystem.sim_dynamics.average(), 2 ) + " msec" - + " update total: " + to_string( Timer::subsystem.sim_total.average(), 2 ) + " msec"; + auto textline = + "vehicles: " + to_string( Timer::subsystem.sim_dynamics.average(), 2 ) + " msec" + + " update total: " + to_string( Timer::subsystem.sim_total.average(), 2 ) + " msec"; - Output.emplace_back( textline, Global.UITextColor ); - // current luminance level - textline = "Cloud cover: " + to_string( Global.Overcast, 3 ); - textline += "\nLight level: " + to_string( Global.fLuminance, 3 ); - if( Global.FakeLight ) { textline += "(*)"; } - textline += "\nAir temperature: " + to_string( Global.AirTemperature, 1 ) + " deg C"; + Output.emplace_back( textline, Global.UITextColor ); + // current luminance level + textline = "Cloud cover: " + to_string( Global.Overcast, 3 ); + textline += "\nLight level: " + to_string( Global.fLuminance, 3 ); + if( Global.FakeLight ) { textline += "(*)"; } + textline += "\nAir temperature: " + to_string( Global.AirTemperature, 1 ) + " deg C"; - Output.emplace_back( textline, Global.UITextColor ); + Output.emplace_back( textline, Global.UITextColor ); } void debug_panel::update_section_eventqueue( std::vector &Output ) { - std::string textline; + std::string textline; - // current event queue - auto const time { Timer::GetTime() }; - auto const *event { simulation::Events.begin() }; + // current event queue + auto const time { Timer::GetTime() }; + auto const *event { simulation::Events.begin() }; - Output.emplace_back( "Delay: Event:", Global.UITextColor ); + Output.emplace_back( "Delay: Event:", Global.UITextColor ); - while( ( event != nullptr ) - && ( Output.size() < 30 ) ) { + while( ( event != nullptr ) + && ( Output.size() < 30 ) ) { - if( ( false == event->m_ignored ) - && ( false == event->m_passive ) - && ( ( false == m_eventqueueactivevehicleonly ) - || ( event->m_activator == m_input.vehicle ) ) ) { + if( ( false == event->m_ignored ) + && ( false == event->m_passive ) + && ( ( false == m_eventqueueactivevehicleonly ) + || ( event->m_activator == m_input.vehicle ) ) ) { - auto const delay { " " + to_string( std::max( 0.0, event->m_launchtime - time ), 1 ) }; - textline = delay.substr( delay.length() - 6 ) - + " " + event->m_name - + ( event->m_activator ? " (by: " + event->m_activator->asName + ")" : "" ) - + ( event->m_sibling ? " (joint event)" : "" ); + auto const delay { " " + to_string( std::max( 0.0, event->m_launchtime - time ), 1 ) }; + textline = delay.substr( delay.length() - 6 ) + + " " + event->m_name + + ( event->m_activator ? " (by: " + event->m_activator->asName + ")" : "" ) + + ( event->m_sibling ? " (joint event)" : "" ); - Output.emplace_back( textline, Global.UITextColor ); - } - event = event->m_next; - } - if( Output.size() == 1 ) { - Output.front().data = "(no queued events)"; - } + Output.emplace_back( textline, Global.UITextColor ); + } + event = event->m_next; + } + if( Output.size() == 1 ) { + Output.front().data = "(no queued events)"; + } +} + +void +debug_panel::update_section_powergrid( std::vector &Output ) { + + auto const lowpowercolor { glm::vec4( 164.0f / 255.0f, 132.0f / 255.0f, 84.0f / 255.0f, 1.f ) }; + auto const nopowercolor { glm::vec4( 164.0f / 255.0f, 84.0f / 255.0f, 84.0f / 255.0f, 1.f ) }; + + Output.emplace_back( "Name: Output: Timeout:", Global.UITextColor ); + + std::string textline; + + for( auto const *powerstation : simulation::Powergrid.sequence() ) { + + if( true == powerstation->bSection ) { continue; } + + auto const name { ( + powerstation->m_name.empty() ? + "(unnamed)" : + powerstation->m_name ) + + " " }; + + textline = + name.substr( 0, 20 ) + + " " + to_string( powerstation->OutputVoltage, 0, 5 ) + + " " + to_string( powerstation->FuseTimer, 1, 12 ) + + ( powerstation->FuseCounter == 0 ? + "" : + " (x" + to_string( powerstation->FuseCounter ) + ")" ); + + Output.emplace_back( + textline, + ( ( powerstation->FastFuse || powerstation->SlowFuse ) ? nopowercolor : + powerstation->OutputVoltage < ( 0.8 * powerstation->NominalVoltage ) ? lowpowercolor : + Global.UITextColor ) ); + } + + if( Output.size() == 1 ) { + Output.front().data = "(no power stations)"; + } } void debug_panel::update_section_camera( std::vector &Output ) { - if( m_input.camera == nullptr ) { return; } + if( m_input.camera == nullptr ) { return; } - auto const &camera{ *m_input.camera }; + auto const &camera{ *m_input.camera }; - // camera data - auto textline = - "Position: [" - + to_string( camera.Pos.x, 2 ) + ", " - + to_string( camera.Pos.y, 2 ) + ", " - + to_string( camera.Pos.z, 2 ) + "]"; + // camera data + auto textline = + "Position: [" + + to_string( camera.Pos.x, 2 ) + ", " + + to_string( camera.Pos.y, 2 ) + ", " + + to_string( camera.Pos.z, 2 ) + "]"; - Output.emplace_back( textline, Global.UITextColor ); + Output.emplace_back( textline, Global.UITextColor ); - textline = - "Azimuth: " - + to_string( 180.0 - glm::degrees( camera.Angle.y ), 0 ) // ma być azymut, czyli 0 na północy i rośnie na wschód - + " " - + std::string( "S SEE NEN NWW SW" ) - .substr( 0 + 2 * floor( fmod( 8 + ( camera.Angle.y + 0.5 * M_PI_4 ) / M_PI_4, 8 ) ), 2 ); + textline = + "Azimuth: " + + to_string( 180.0 - glm::degrees( camera.Angle.y ), 0 ) // ma być azymut, czyli 0 na północy i rośnie na wschód + + " " + + std::string( "S SEE NEN NWW SW" ) + .substr( 0 + 2 * floor( fmod( 8 + ( camera.Angle.y + 0.5 * M_PI_4 ) / M_PI_4, 8 ) ), 2 ); - Output.emplace_back( textline, Global.UITextColor ); + Output.emplace_back( textline, Global.UITextColor ); } void debug_panel::update_section_renderer( std::vector &Output ) { // gfx renderer data - auto textline = - "FoV: " + to_string( Global.FieldOfView / Global.ZoomFactor, 1 ) - + ", Draw range x " + to_string( Global.fDistanceFactor, 1 ) + auto textline = + "FoV: " + to_string( Global.FieldOfView / Global.ZoomFactor, 1 ) + + ", Draw range x " + to_string( Global.fDistanceFactor, 1 ) // + "; sectors: " + std::to_string( GfxRenderer.m_drawcount ) // + ", FPS: " + to_string( Timer::GetFPS(), 2 ); - + ", FPS: " + std::to_string( static_cast(std::round(GfxRenderer.Framerate())) ); - if( Global.iSlowMotion ) { - textline += " (slowmotion " + to_string( Global.iSlowMotion ) + ")"; - } + + ", FPS: " + std::to_string( static_cast(std::round(GfxRenderer.Framerate())) ); + if( Global.iSlowMotion ) { + textline += " (slowmotion " + to_string( Global.iSlowMotion ) + ")"; + } - Output.emplace_back( textline, Global.UITextColor ); + Output.emplace_back( textline, Global.UITextColor ); - textline = ""; - if( false == Global.LastGLError.empty() ) { - textline += - "Last openGL error: " - + Global.LastGLError; - } + textline = ""; + if( false == Global.LastGLError.empty() ) { + textline += + "Last openGL error: " + + Global.LastGLError; + } - Output.emplace_back( textline, Global.UITextColor ); + Output.emplace_back( textline, Global.UITextColor ); // renderer stats - Output.emplace_back( GfxRenderer.info_times(), Global.UITextColor ); - Output.emplace_back( GfxRenderer.info_stats(), Global.UITextColor ); + Output.emplace_back( GfxRenderer.info_times(), Global.UITextColor ); + Output.emplace_back( GfxRenderer.info_stats(), Global.UITextColor ); } bool debug_panel::render_section( std::string const &Header, std::vector const &Lines ) { - if( true == Lines.empty() ) { return false; } - if( false == ImGui::CollapsingHeader( Header.c_str() ) ) { return false; } + if( true == Lines.empty() ) { return false; } + if( false == ImGui::CollapsingHeader( Header.c_str() ) ) { return false; } - for( auto const &line : Lines ) { - ImGui::PushStyleColor( ImGuiCol_Text, { line.color.r, line.color.g, line.color.b, line.color.a } ); - ImGui::TextUnformatted( line.data.c_str() ); - ImGui::PopStyleColor(); + for( auto const &line : Lines ) { + ImGui::PushStyleColor( ImGuiCol_Text, { line.color.r, line.color.g, line.color.b, line.color.a } ); + ImGui::TextUnformatted( line.data.c_str() ); + ImGui::PopStyleColor(); // ImGui::TextColored( ImVec4( line.color.r, line.color.g, line.color.b, line.color.a ), line.data.c_str() ); - } - return true; + } + return true; } void transcripts_panel::update() { - if( false == is_open ) { return; } + if( false == is_open ) { return; } - text_lines.clear(); + text_lines.clear(); - for( auto const &transcript : ui::Transcripts.aLines ) { - if( Global.fTimeAngleDeg + ( transcript.fShow - Global.fTimeAngleDeg > 180 ? 360 : 0 ) < transcript.fShow ) { continue; } - text_lines.emplace_back( ExchangeCharInString( transcript.asText, '|', ' ' ), colors::white ); - } + for( auto const &transcript : ui::Transcripts.aLines ) { + if( Global.fTimeAngleDeg + ( transcript.fShow - Global.fTimeAngleDeg > 180 ? 360 : 0 ) < transcript.fShow ) { continue; } + text_lines.emplace_back( ExchangeCharInString( transcript.asText, '|', ' ' ), colors::white ); + } } void diff --git a/driveruipanels.h b/driveruipanels.h index b551aa11..7fd2d338 100644 --- a/driveruipanels.h +++ b/driveruipanels.h @@ -85,6 +85,7 @@ private: void update_section_scantable( std::vector &Output ); void update_section_scenario( std::vector &Output ); void update_section_eventqueue( std::vector &Output ); + void update_section_powergrid( std::vector &Output ); void update_section_camera( std::vector &Output ); void update_section_renderer( std::vector &Output ); // section update helpers @@ -103,6 +104,7 @@ private: m_cameralines, m_scenariolines, m_eventqueuelines, + m_powergridlines, m_rendererlines; int tprev { 0 }; // poprzedni czas double VelPrev { 0.0 }; // poprzednia prędkość diff --git a/mtable.cpp b/mtable.cpp index 56b32a5e..6bcb0677 100644 --- a/mtable.cpp +++ b/mtable.cpp @@ -550,6 +550,7 @@ void TTrainParameters::serialize( dictionary_source *Output ) const { Output->insert( ( stationlabel + "am" ), timetableline.Am ); Output->insert( ( stationlabel + "dh" ), timetableline.Dh ); Output->insert( ( stationlabel + "dm" ), timetableline.Dm ); + Output->insert( ( stationlabel + "tracks" ), timetableline.TrackNo ); } } } diff --git a/renderer.h b/renderer.h index db2994fe..4ecc7e82 100644 --- a/renderer.h +++ b/renderer.h @@ -124,8 +124,7 @@ class opengl_renderer /// Renderer runtime settings struct Settings { - /** Force normal render of traction, when user is in debug mode. */ - bool force_normal_traction_render { false }; + bool traction_debug { false }; } settings; // methods diff --git a/simulationstateserializer.cpp b/simulationstateserializer.cpp index 825dba88..b7b82c38 100644 --- a/simulationstateserializer.cpp +++ b/simulationstateserializer.cpp @@ -531,7 +531,10 @@ state_serializer::deserialize_node( cParser &Input, scene::scratch_data &Scratch } else { scene::Groups.insert( scene::Groups.handle(), eventlauncher ); - simulation::Region->insert( eventlauncher ); + if( false == eventlauncher->IsRadioActivated() ) { + // NOTE: radio-activated launchers due to potentially large activation radius are resolved on global level rather than put in a region cell + simulation::Region->insert( eventlauncher ); + } } } else if( nodedata.type == "sound" ) { diff --git a/translation.cpp b/translation.cpp index f0ad76cd..c8853d9a 100644 --- a/translation.cpp +++ b/translation.cpp @@ -246,6 +246,7 @@ std::string locale::label_cab_control(std::string const &Label) { "radiochannelnext_sw:", STRN("radio channel") }, { "radiotest_sw:", STRN("radiostop test") }, { "radiostop_sw:", STRN("radiostop") }, + { "radiocall3_sw:", STRN("radio call 3") }, { "pantfront_sw:", STRN("pantograph A") }, { "pantrear_sw:", STRN("pantograph B") }, { "pantfrontoff_sw:", STRN("pantograph A") }, diff --git a/version.h b/version.h index f62e83b0..482a8e66 100644 --- a/version.h +++ b/version.h @@ -1 +1 @@ -#define VERSION_INFO "M7 (sim) 23.05.2019" +#define VERSION_INFO "M7 (sim) 13.07.2019" diff --git a/widgets/vehicleparams.cpp b/widgets/vehicleparams.cpp index dec2f11e..e39df5f5 100644 --- a/widgets/vehicleparams.cpp +++ b/widgets/vehicleparams.cpp @@ -74,9 +74,9 @@ void ui::vehicleparams_panel::render_contents() // power transfers mover.Couplers[ end::front ].power_high.voltage, mover.Couplers[ end::front ].power_high.current, - std::string( mover.Couplers[ end::front ].power_high.local ? "" : "-" ).c_str(), + std::string( mover.Couplers[ end::front ].power_high.is_local ? "" : "-" ).c_str(), std::string( vehicle.DirectionGet() ? ":<<:" : ":>>:" ).c_str(), - std::string( mover.Couplers[ end::rear ].power_high.local ? "" : "-" ).c_str(), + std::string( mover.Couplers[ end::rear ].power_high.is_local ? "" : "-" ).c_str(), mover.Couplers[ end::rear ].power_high.voltage, mover.Couplers[ end::rear ].power_high.current );