diff --git a/Driver.cpp b/Driver.cpp index 2070329d..3f510769 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 } @@ -2131,7 +2131,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 ); @@ -2266,6 +2269,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; } @@ -2454,7 +2463,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; } @@ -2527,14 +2536,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 ) ); @@ -2548,6 +2557,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 @@ -2630,7 +2647,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 ); @@ -3125,6 +3144,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 ) ) { @@ -3187,6 +3214,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) @@ -3510,6 +3543,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; @@ -3569,6 +3660,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 @@ -4700,7 +4813,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 } } @@ -5067,6 +5180,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)) @@ -5074,6 +5188,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 ) ); } @@ -5321,7 +5436,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 42bc47f0..3fcb09a5 100644 --- a/Driver.h +++ b/Driver.h @@ -256,6 +256,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 c28a729a..f4d25c0e 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -2689,10 +2689,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) @@ -2745,7 +2746,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); @@ -5774,23 +5782,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; } @@ -6622,7 +6635,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 015c2312..eefd27d5 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; } @@ -208,6 +223,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 68fdc2c0..a221bed8 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 ) { @@ -1958,6 +1958,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() { @@ -2181,32 +2197,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 + "\"" ); + } } } } @@ -2222,8 +2245,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 ); } @@ -2233,7 +2262,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 a6825159..cd034417 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,7 +591,10 @@ public: inline bool insert( TEventLauncher *Launcher ) { - return m_launchers.insert( Launcher ); } + return ( + Launcher->IsRadioActivated() ? + m_radiodrivenlaunchers.insert( Launcher ) : + m_inputdrivenlaunchers.insert( Launcher ) ); } // returns first event in the queue inline basic_event * @@ -637,7 +643,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 a4eb5b85..ec447eff 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 0bc40d96..69c01757 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -387,7 +387,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*/ @@ -440,10 +440,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; @@ -453,7 +465,7 @@ struct _mover__1 //} }; -struct _mover__2 +struct TPowerCable { TPowerType PowerTrans; double SteamPressure; @@ -463,12 +475,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 { @@ -480,11 +497,11 @@ struct TPowerParameters { struct { - _mover__3 RHeater; + THeater RHeater; }; struct { - _mover__2 RPowerCable; + TPowerCable RPowerCable; }; struct { @@ -492,15 +509,15 @@ struct TPowerParameters }; struct { - _mover__1 RAccumulator; + TAccumulator RAccumulator; }; struct { - TEngineType GeneratorEngine; + engine_generator EngineGenerator; }; struct { - double InputVoltage; + TTransducer Transducer; }; struct { @@ -618,7 +635,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 { @@ -1022,7 +1040,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; @@ -1050,6 +1072,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; // @@ -1081,10 +1104,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 { @@ -1225,6 +1248,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; @@ -1290,6 +1315,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*/ @@ -1370,6 +1396,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 @@ -1503,7 +1530,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 ); @@ -1543,8 +1572,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 a08ae247..20751efd 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -673,7 +673,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 @@ -1070,85 +1070,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; @@ -1388,10 +1309,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 ) ) { @@ -1399,6 +1321,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: @@ -1436,6 +1364,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) @@ -1492,12 +1559,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 @@ -2765,6 +2890,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 ) @@ -3892,7 +4018,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); @@ -4031,9 +4156,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( @@ -4423,7 +4549,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 @@ -4577,7 +4703,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; } @@ -4760,6 +4886,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; @@ -5300,12 +5428,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; @@ -6124,9 +6252,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 ) ) @@ -6249,6 +6402,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) { @@ -6380,7 +6549,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 ); @@ -6392,7 +6561,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 }; @@ -6409,11 +6578,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: @@ -6452,7 +6640,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; } } @@ -6478,6 +6666,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; @@ -6495,10 +6685,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); @@ -6596,6 +6787,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 ) ) { @@ -6608,6 +6801,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 ) { @@ -6917,8 +7144,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 ); @@ -7376,15 +7603,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 ) ); } @@ -7826,13 +8049,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; @@ -7840,8 +8069,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 ) { @@ -7875,7 +8109,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; @@ -8088,7 +8322,7 @@ bool TMoverParameters::LoadFIZ(std::string chkpath) startBPT = false; fizlines.emplace("UCList", inputline); startUCLIST = true; LISTLINE = 0; - LoadFIZ_RList(inputline); + LoadFIZ_UCList(inputline); continue; } @@ -8142,7 +8376,7 @@ bool TMoverParameters::LoadFIZ(std::string chkpath) continue; } if (true == startUCLIST) { - readRList(inputline); + readUCList(inputline); continue; } if( true == startDLIST ) { @@ -8162,8 +8396,9 @@ bool TMoverParameters::LoadFIZ(std::string chkpath) continue; } } // while line +/* in.close(); - +*/ // Operacje na zebranych parametrach - przypisywanie do wlasciwych zmiennych i ustawianie // zaleznosci @@ -8774,6 +9009,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 { @@ -8811,6 +9048,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 ) ); @@ -8849,9 +9089,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, ""); @@ -8859,10 +9099,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, ""); } @@ -8909,12 +9149,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; @@ -8970,6 +9211,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; @@ -9004,6 +9258,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) { @@ -9014,6 +9269,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; @@ -9237,12 +9493,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: { @@ -9328,7 +9603,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 @@ -9347,7 +9623,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 @@ -9565,6 +9842,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 7c0c89b9..a274598e 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" @@ -339,6 +340,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 ); @@ -4722,10 +4726,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 ); } @@ -4736,10 +4740,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 ); @@ -5015,6 +5019,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(); } @@ -5027,6 +5032,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 ) { @@ -5041,6 +5063,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 ); + } } } @@ -5058,6 +5084,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 ); + } } } @@ -5150,10 +5180,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; @@ -5676,9 +5707,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 ) ); @@ -5910,7 +5942,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( @@ -6165,6 +6197,7 @@ bool TTrain::Update( double const Deltatime ) ggRadioChannelNext.Update(); ggRadioStop.Update(); ggRadioTest.Update(); + ggRadioCall3.Update(); ggDepartureSignalButton.Update(); ggPantFrontButton.Update(); @@ -7494,6 +7527,7 @@ void TTrain::clear_cab_controls() ggRadioChannelNext.Clear(); ggRadioStop.Clear(); ggRadioTest.Clear(); + ggRadioCall3.Clear(); ggDoorLeftPermitButton.Clear(); ggDoorRightPermitButton.Clear(); ggDoorPermitPresetButton.Clear(); @@ -7811,7 +7845,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 ); } @@ -7819,6 +7853,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 @@ -8179,6 +8214,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 fc472d43..5dcb06c7 100644 --- a/Train.h +++ b/Train.h @@ -343,6 +343,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 ); @@ -417,6 +418,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 63efc208..dd657dd7 100644 --- a/TrkFoll.cpp +++ b/TrkFoll.cpp @@ -95,7 +95,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 94d810a9..235f0252 100644 --- a/command.cpp +++ b/command.cpp @@ -124,6 +124,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 ad7f783e..e234da7e 100644 --- a/command.h +++ b/command.h @@ -118,6 +118,7 @@ enum class user_command { radiochanneldecrease, radiostopsend, radiostoptest, + radiocall3send, cabchangeforward, cabchangebackward, diff --git a/driverkeyboardinput.cpp b/driverkeyboardinput.cpp index 471e11d9..c34e79d1 100644 --- a/driverkeyboardinput.cpp +++ b/driverkeyboardinput.cpp @@ -128,6 +128,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 f4f5cbdf..078d39f1 100644 --- a/driveruilayer.cpp +++ b/driveruilayer.cpp @@ -41,7 +41,7 @@ driver_ui::driver_ui() { m_scenariopanel.size_max = { Global.iWindowWidth * 0.95, Global.iWindowHeight * 0.95 }; m_timetablepanel.title = locale::strings[ locale::string::driver_timetable_header ]; - m_timetablepanel.size_min = { 435, 110 }; + m_timetablepanel.size_min = { 435, 70 }; m_timetablepanel.size_max = { 435, Global.iWindowHeight * 0.95 }; m_transcriptspanel.title = locale::strings[ locale::string::driver_transcripts_header ]; diff --git a/driveruipanels.cpp b/driveruipanels.cpp index c9e021df..5befece4 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" @@ -30,1072 +31,1123 @@ http://mozilla.org/MPL/2.0/. void drivingaid_panel::update() { - if( false == is_open ) { return; } + if( false == is_open ) { return; } - text_lines.clear(); + text_lines.clear(); - auto const *train { simulation::Train }; - auto const *controlled { ( train ? train->Dynamic() : nullptr ) }; + auto const *train { simulation::Train }; + auto const *controlled { ( train ? train->Dynamic() : nullptr ) }; - if( ( controlled == nullptr ) - || ( controlled->Mechanik == nullptr ) ) { return; } + if( ( controlled == nullptr ) + || ( controlled->Mechanik == nullptr ) ) { return; } - auto const *mover = controlled->MoverParameters; - auto const *driver = controlled->Mechanik; + auto const *mover = controlled->MoverParameters; + auto const *driver = controlled->Mechanik; - { // throttle, velocity, speed limits and grade - std::string expandedtext; - if( is_expanded ) { - // grade - std::string gradetext; - auto const reverser { ( mover->ActiveDir > 0 ? 1 : -1 ) }; - auto const grade { controlled->VectorFront().y * 100 * ( controlled->DirectionGet() == reverser ? 1 : -1 ) * reverser }; - if( std::abs( grade ) >= 0.25 ) { - std::snprintf( - m_buffer.data(), m_buffer.size(), - locale::strings[ locale::string::driver_aid_grade ].c_str(), - grade ); - gradetext = m_buffer.data(); - } - // next speed limit - auto const speedlimit { static_cast( std::floor( driver->VelDesired ) ) }; - auto const nextspeedlimit { static_cast( std::floor( driver->VelNext ) ) }; - std::string nextspeedlimittext; - if( nextspeedlimit != speedlimit ) { - std::snprintf( - m_buffer.data(), m_buffer.size(), - locale::strings[ locale::string::driver_aid_nextlimit ].c_str(), - nextspeedlimit, - driver->ActualProximityDist * 0.001 ); - nextspeedlimittext = m_buffer.data(); - } - // current speed and limit - std::snprintf( - m_buffer.data(), m_buffer.size(), - locale::strings[ locale::string::driver_aid_speedlimit ].c_str(), - static_cast( std::floor( mover->Vel ) ), - speedlimit, - nextspeedlimittext.c_str(), - gradetext.c_str() ); - expandedtext = m_buffer.data(); - } - // base data and optional bits put together - std::snprintf( - m_buffer.data(), m_buffer.size(), - locale::strings[ locale::string::driver_aid_throttle ].c_str(), - driver->Controlling()->MainCtrlPos, - driver->Controlling()->ScndCtrlPos, - ( mover->ActiveDir > 0 ? 'D' : mover->ActiveDir < 0 ? 'R' : 'N' ), - expandedtext.c_str()); + { // throttle, velocity, speed limits and grade + std::string expandedtext; + if( is_expanded ) { + // grade + std::string gradetext; + auto const reverser { ( mover->ActiveDir > 0 ? 1 : -1 ) }; + auto const grade { controlled->VectorFront().y * 100 * ( controlled->DirectionGet() == reverser ? 1 : -1 ) * reverser }; + if( std::abs( grade ) >= 0.25 ) { + std::snprintf( + m_buffer.data(), m_buffer.size(), + locale::strings[ locale::string::driver_aid_grade ].c_str(), + grade ); + gradetext = m_buffer.data(); + } + // next speed limit + auto const speedlimit { static_cast( std::floor( driver->VelDesired ) ) }; + auto const nextspeedlimit { static_cast( std::floor( driver->VelNext ) ) }; + std::string nextspeedlimittext; + if( nextspeedlimit != speedlimit ) { + std::snprintf( + m_buffer.data(), m_buffer.size(), + locale::strings[ locale::string::driver_aid_nextlimit ].c_str(), + nextspeedlimit, + driver->ActualProximityDist * 0.001 ); + nextspeedlimittext = m_buffer.data(); + } + // current speed and limit + std::snprintf( + m_buffer.data(), m_buffer.size(), + locale::strings[ locale::string::driver_aid_speedlimit ].c_str(), + static_cast( std::floor( mover->Vel ) ), + speedlimit, + nextspeedlimittext.c_str(), + gradetext.c_str() ); + expandedtext = m_buffer.data(); + } + // base data and optional bits put together + std::snprintf( + m_buffer.data(), m_buffer.size(), + locale::strings[ locale::string::driver_aid_throttle ].c_str(), + driver->Controlling()->MainCtrlPos, + driver->Controlling()->ScndCtrlPos, + ( mover->ActiveDir > 0 ? 'D' : mover->ActiveDir < 0 ? 'R' : 'N' ), + expandedtext.c_str()); - text_lines.emplace_back( m_buffer.data(), Global.UITextColor ); - } + text_lines.emplace_back( m_buffer.data(), Global.UITextColor ); + } - { // brakes, air pressure - std::string expandedtext; - if( is_expanded ) { - std::snprintf ( - m_buffer.data(), m_buffer.size(), - locale::strings[ locale::string::driver_aid_pressures ].c_str(), - mover->BrakePress * 100, - mover->PipePress * 100 ); - expandedtext = m_buffer.data(); - } - std::snprintf( - m_buffer.data(), m_buffer.size(), - locale::strings[ locale::string::driver_aid_brakes ].c_str(), - mover->fBrakeCtrlPos, - mover->LocalBrakePosA * LocalBrakePosNo, - ( mover->SlippingWheels ? '!' : ' ' ), - expandedtext.c_str() ); + { // brakes, air pressure + std::string expandedtext; + if( is_expanded ) { + std::snprintf ( + m_buffer.data(), m_buffer.size(), + locale::strings[ locale::string::driver_aid_pressures ].c_str(), + mover->BrakePress * 100, + mover->PipePress * 100 ); + expandedtext = m_buffer.data(); + } + std::snprintf( + m_buffer.data(), m_buffer.size(), + locale::strings[ locale::string::driver_aid_brakes ].c_str(), + mover->fBrakeCtrlPos, + mover->LocalBrakePosA * LocalBrakePosNo, + ( mover->SlippingWheels ? '!' : ' ' ), + expandedtext.c_str() ); - text_lines.emplace_back( m_buffer.data(), Global.UITextColor ); - } + text_lines.emplace_back( m_buffer.data(), Global.UITextColor ); + } - { // alerter, hints - std::string expandedtext; - if( is_expanded ) { - auto const stoptime { static_cast( std::ceil( -1.0 * controlled->Mechanik->fStopTime ) ) }; - if( stoptime > 0 ) { - std::snprintf( - m_buffer.data(), m_buffer.size(), - locale::strings[ locale::string::driver_aid_loadinginprogress ].c_str(), - stoptime ); - expandedtext = m_buffer.data(); - } - else { - auto const trackblockdistance{ std::abs( controlled->Mechanik->TrackBlock() ) }; - if( trackblockdistance <= 75.0 ) { - std::snprintf( - m_buffer.data(), m_buffer.size(), - locale::strings[ locale::string::driver_aid_vehicleahead ].c_str(), - trackblockdistance ); - expandedtext = m_buffer.data(); - } - } - } - std::string textline = - ( true == TestFlag( mover->SecuritySystem.Status, s_aware ) ? - locale::strings[ locale::string::driver_aid_alerter ] : - " " ); - textline += - ( true == TestFlag( mover->SecuritySystem.Status, s_active ) ? - locale::strings[ locale::string::driver_aid_shp ] : - " " ); + { // alerter, hints + std::string expandedtext; + if( is_expanded ) { + auto const stoptime { static_cast( std::ceil( -1.0 * controlled->Mechanik->fStopTime ) ) }; + if( stoptime > 0 ) { + std::snprintf( + m_buffer.data(), m_buffer.size(), + locale::strings[ locale::string::driver_aid_loadinginprogress ].c_str(), + stoptime ); + expandedtext = m_buffer.data(); + } + else { + auto const trackblockdistance{ std::abs( controlled->Mechanik->TrackBlock() ) }; + if( trackblockdistance <= 75.0 ) { + std::snprintf( + m_buffer.data(), m_buffer.size(), + locale::strings[ locale::string::driver_aid_vehicleahead ].c_str(), + trackblockdistance ); + expandedtext = m_buffer.data(); + } + } + } + std::string textline = + ( true == TestFlag( mover->SecuritySystem.Status, s_aware ) ? + locale::strings[ locale::string::driver_aid_alerter ] : + " " ); + textline += + ( true == TestFlag( mover->SecuritySystem.Status, s_active ) ? + locale::strings[ locale::string::driver_aid_shp ] : + " " ); - text_lines.emplace_back( textline + " " + expandedtext, Global.UITextColor ); - } + text_lines.emplace_back( textline + " " + expandedtext, Global.UITextColor ); + } } void scenario_panel::update() { - if( false == is_open ) { return; } + if( false == is_open ) { return; } - text_lines.clear(); + text_lines.clear(); - auto const *train { simulation::Train }; - auto const *controlled { ( train ? train->Dynamic() : nullptr ) }; - auto const &camera { Global.pCamera }; - m_nearest = ( - 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( m_nearest == nullptr ) { return; } - auto const *owner { ( - ( ( m_nearest->Mechanik != nullptr ) && ( m_nearest->Mechanik->Primary() ) ) ? - m_nearest->Mechanik : - m_nearest->ctOwner ) }; - if( owner == nullptr ) { return; } + auto const *train { simulation::Train }; + auto const *controlled { ( train ? train->Dynamic() : nullptr ) }; + auto const &camera { Global.pCamera }; + m_nearest = ( + 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( m_nearest == nullptr ) { return; } + auto const *owner { ( + ( ( m_nearest->Mechanik != nullptr ) && ( m_nearest->Mechanik->Primary() ) ) ? + m_nearest->Mechanik : + m_nearest->ctOwner ) }; + if( owner == nullptr ) { return; } - std::string textline = - locale::strings[ locale::string::driver_scenario_currenttask ] + "\n " - + owner->OrderCurrent(); + std::string textline = + locale::strings[ locale::string::driver_scenario_currenttask ] + "\n " + + owner->OrderCurrent(); - text_lines.emplace_back( textline, Global.UITextColor ); + text_lines.emplace_back( textline, Global.UITextColor ); } void scenario_panel::render() { - if( false == is_open ) { return; } - if( true == text_lines.empty() ) { return; } - if( m_nearest == nullptr ) { return; } // possibly superfluous given the above but, eh + if( false == is_open ) { return; } + if( true == text_lines.empty() ) { return; } + if( m_nearest == nullptr ) { return; } // possibly superfluous given the above but, eh - auto flags = - ImGuiWindowFlags_NoFocusOnAppearing - | ImGuiWindowFlags_NoCollapse - | ( size.x > 0 ? ImGuiWindowFlags_NoResize : 0 ); + auto flags = + ImGuiWindowFlags_NoFocusOnAppearing + | ImGuiWindowFlags_NoCollapse + | ( size.x > 0 ? ImGuiWindowFlags_NoResize : 0 ); - if( size.x > 0 ) { - ImGui::SetNextWindowSize( ImVec2( size.x, size.y ) ); - } - if( size_min.x > 0 ) { - ImGui::SetNextWindowSizeConstraints( ImVec2( size_min.x, size_min.y ), ImVec2( size_max.x, size_max.y ) ); - } - auto const panelname { ( - title.empty() ? - name : - title ) - + "###" + name }; - if( true == ImGui::Begin( panelname.c_str(), &is_open, flags ) ) { - // potential assignment section - auto const *owner { ( - ( ( m_nearest->Mechanik != nullptr ) && ( m_nearest->Mechanik->Primary() ) ) ? - m_nearest->Mechanik : - m_nearest->ctOwner ) }; - if( owner != nullptr ) { - auto const assignmentheader { locale::strings[ locale::string::driver_scenario_assignment ] }; - if( ( false == owner->assignment().empty() ) - && ( true == ImGui::CollapsingHeader( assignmentheader.c_str() ) ) ) { - ImGui::TextWrapped( "%s", owner->assignment().c_str() ); - ImGui::Separator(); - } - } - // current task - for( auto const &line : text_lines ) { - ImGui::TextColored( ImVec4( line.color.r, line.color.g, line.color.b, line.color.a ), line.data.c_str() ); - } - } - ImGui::End(); + if( size.x > 0 ) { + ImGui::SetNextWindowSize( ImVec2( size.x, size.y ) ); + } + if( size_min.x > 0 ) { + ImGui::SetNextWindowSizeConstraints( ImVec2( size_min.x, size_min.y ), ImVec2( size_max.x, size_max.y ) ); + } + auto const panelname { ( + title.empty() ? + name : + title ) + + "###" + name }; + if( true == ImGui::Begin( panelname.c_str(), &is_open, flags ) ) { + // potential assignment section + auto const *owner { ( + ( ( m_nearest->Mechanik != nullptr ) && ( m_nearest->Mechanik->Primary() ) ) ? + m_nearest->Mechanik : + m_nearest->ctOwner ) }; + if( owner != nullptr ) { + auto const assignmentheader { locale::strings[ locale::string::driver_scenario_assignment ] }; + if( ( false == owner->assignment().empty() ) + && ( true == ImGui::CollapsingHeader( assignmentheader.c_str() ) ) ) { + ImGui::TextWrapped( "%s", owner->assignment().c_str() ); + ImGui::Separator(); + } + } + // current task + for( auto const &line : text_lines ) { + ImGui::TextColored( ImVec4( line.color.r, line.color.g, line.color.b, line.color.a ), line.data.c_str() ); + } + } + ImGui::End(); } 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( - m_buffer.data(), m_buffer.size(), - locale::strings[ locale::string::driver_timetable_header ].c_str(), - 37, 37, - locale::strings[ locale::string::driver_timetable_name ].c_str(), - time.wHour, - time.wMinute, - time.wSecond ); + { // current time + std::snprintf( + m_buffer.data(), m_buffer.size(), + locale::strings[ locale::string::driver_timetable_header ].c_str(), + 37, 37, + locale::strings[ locale::string::driver_timetable_name ].c_str(), + time.wHour, + 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(), - locale::strings[ locale::string::driver_timetable_consistdata ].c_str(), - static_cast( table->LocLoad ), - static_cast( consistmass / 1000 ), - static_cast( consistlength ) ); + std::snprintf( + m_buffer.data(), m_buffer.size(), + locale::strings[ locale::string::driver_timetable_consistdata ].c_str(), + static_cast( table->LocLoad ), + static_cast( consistmass / 1000 ), + static_cast( consistlength ) ); - text_lines.emplace_back( m_buffer.data(), Global.UITextColor ); - text_lines.emplace_back( "", Global.UITextColor ); - } + text_lines.emplace_back( m_buffer.data(), Global.UITextColor ); + } - if( 0 == table->StationCount ) { - // only bother if there's stations to list - text_lines.emplace_back( locale::strings[ locale::string::driver_timetable_notimetable ], Global.UITextColor ); - } - else { + if( 0 == table->StationCount ) { + // only bother if there's stations to list + text_lines.emplace_back( locale::strings[ locale::string::driver_timetable_notimetable ], Global.UITextColor ); + } + else { - auto const loadingcolor { glm::vec4( 164.0f / 255.0f, 84.0f / 255.0f, 84.0f / 255.0f, 1.f ) }; - auto const waitcolor { glm::vec4( 164.0f / 255.0f, 132.0f / 255.0f, 84.0f / 255.0f, 1.f ) }; - auto const readycolor { glm::vec4( 84.0f / 255.0f, 164.0f / 255.0f, 132.0f / 255.0f, 1.f ) }; + auto const loadingcolor { glm::vec4( 164.0f / 255.0f, 84.0f / 255.0f, 84.0f / 255.0f, 1.f ) }; + auto const waitcolor { glm::vec4( 164.0f / 255.0f, 132.0f / 255.0f, 84.0f / 255.0f, 1.f ) }; + 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 timetable_panel::render() { - if( false == is_open ) { return; } - if( true == text_lines.empty() ) { return; } + if( false == is_open ) { return; } + if( true == text_lines.empty() ) { return; } - auto flags = - ImGuiWindowFlags_NoFocusOnAppearing - | ImGuiWindowFlags_NoCollapse - | ( size.x > 0 ? ImGuiWindowFlags_NoResize : 0 ); + auto flags = + ImGuiWindowFlags_NoFocusOnAppearing + | ImGuiWindowFlags_NoCollapse + | ( size.x > 0 ? ImGuiWindowFlags_NoResize : 0 ); - if( size.x > 0 ) { - ImGui::SetNextWindowSize( ImVec2( size.x, size.y ) ); - } - if( size_min.x > 0 ) { - ImGui::SetNextWindowSizeConstraints( ImVec2( size_min.x, size_min.y ), ImVec2( size_max.x, size_max.y ) ); - } - auto const panelname { ( - title.empty() ? - name : - title ) - + "###" + name }; - if( true == ImGui::Begin( panelname.c_str(), &is_open, flags ) ) { - for( auto const &line : text_lines ) { - ImGui::TextColored( ImVec4( line.color.r, line.color.g, line.color.b, line.color.a ), line.data.c_str() ); - } - if( is_expanded ) { - ImGui::PushStyleVar( ImGuiStyleVar_ItemSpacing, ImVec2( 1, 0 ) ); - for( auto const &line : m_tablelines ) { - ImGui::TextColored( ImVec4( line.color.r, line.color.g, line.color.b, line.color.a ), line.data.c_str() ); - } - ImGui::PopStyleVar(); - } - } - ImGui::End(); + if( size.x > 0 ) { + ImGui::SetNextWindowSize( ImVec2( size.x, size.y ) ); + } + if( size_min.x > 0 ) { + ImGui::SetNextWindowSizeConstraints( ImVec2( size_min.x, size_min.y ), ImVec2( size_max.x, size_max.y ) ); + } + auto const panelname { ( + title.empty() ? + name : + title ) + + "###" + name }; + if( true == ImGui::Begin( panelname.c_str(), &is_open, flags ) ) { + for( auto const &line : text_lines ) { + ImGui::TextColored( ImVec4( line.color.r, line.color.g, line.color.b, line.color.a ), line.data.c_str() ); + } + if( is_expanded ) { + ImGui::PushStyleVar( ImGuiStyleVar_ItemSpacing, ImVec2( 1, 0 ) ); + for( auto const &line : m_tablelines ) { + ImGui::TextColored( ImVec4( line.color.r, line.color.g, line.color.b, line.color.a ), line.data.c_str() ); + } + ImGui::PopStyleVar(); + } + } + ImGui::End(); } 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 ); } void debug_panel::render() { - if( false == is_open ) { return; } + if( false == is_open ) { return; } - auto flags = - ImGuiWindowFlags_NoFocusOnAppearing - | ImGuiWindowFlags_NoCollapse - | ( size.x > 0 ? ImGuiWindowFlags_NoResize : 0 ); + auto flags = + ImGuiWindowFlags_NoFocusOnAppearing + | ImGuiWindowFlags_NoCollapse + | ( size.x > 0 ? ImGuiWindowFlags_NoResize : 0 ); - if( size.x > 0 ) { - ImGui::SetNextWindowSize( ImVec2( size.x, size.y ) ); - } - if( size_min.x > 0 ) { - ImGui::SetNextWindowSizeConstraints( ImVec2( size_min.x, size_min.y ), ImVec2( size_max.x, size_max.y ) ); - } - auto const panelname { ( - title.empty() ? - name : - title ) - + "###" + name }; - if( true == ImGui::Begin( panelname.c_str(), &is_open, flags ) ) { - // header section - for( auto const &line : text_lines ) { - ImGui::TextColored( ImVec4( line.color.r, line.color.g, line.color.b, line.color.a ), line.data.c_str() ); - } - // sections - ImGui::Separator(); + if( size.x > 0 ) { + ImGui::SetNextWindowSize( ImVec2( size.x, size.y ) ); + } + if( size_min.x > 0 ) { + ImGui::SetNextWindowSizeConstraints( ImVec2( size_min.x, size_min.y ), ImVec2( size_max.x, size_max.y ) ); + } + auto const panelname { ( + title.empty() ? + name : + title ) + + "###" + name }; + if( true == ImGui::Begin( panelname.c_str(), &is_open, flags ) ) { + // header section + for( auto const &line : text_lines ) { + ImGui::TextColored( ImVec4( line.color.r, line.color.g, line.color.b, line.color.a ), line.data.c_str() ); + } + // sections + ImGui::Separator(); render_section( "Vehicle", m_vehiclelines ); render_section( "Vehicle Engine", m_enginelines ); render_section( "Vehicle AI", m_ailines ); 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 debug_panel::update_section_vehicle( std::vector &Output ) { - if( m_input.vehicle == nullptr ) { return; } - if( m_input.mover == nullptr ) { return; } + if( m_input.vehicle == nullptr ) { return; } + if( m_input.mover == nullptr ) { return; } - auto const &vehicle { *m_input.vehicle }; - auto const &mover { *m_input.mover }; + auto const &vehicle { *m_input.vehicle }; + auto const &mover { *m_input.mover }; - auto const isowned { ( vehicle.Mechanik == nullptr ) && ( vehicle.ctOwner != nullptr ) }; - auto const isplayervehicle { ( m_input.train != nullptr ) && ( m_input.train->Dynamic() == m_input.vehicle ) }; - auto const isdieselenginepowered { ( mover.EngineType == TEngineType::DieselElectric ) || ( mover.EngineType == TEngineType::DieselEngine ) }; - auto const isdieselinshuntmode { mover.ShuntMode && mover.EngineType == TEngineType::DieselElectric }; + auto const isowned { ( vehicle.Mechanik == nullptr ) && ( vehicle.ctOwner != nullptr ) }; + auto const isplayervehicle { ( m_input.train != nullptr ) && ( m_input.train->Dynamic() == m_input.vehicle ) }; + auto const isdieselenginepowered { ( mover.EngineType == TEngineType::DieselElectric ) || ( mover.EngineType == TEngineType::DieselEngine ) }; + auto const isdieselinshuntmode { mover.ShuntMode && mover.EngineType == TEngineType::DieselElectric }; - std::snprintf( - m_buffer.data(), m_buffer.size(), - locale::strings[ locale::string::debug_vehicle_nameloadstatuscouplers ].c_str(), - mover.Name.c_str(), - std::string( isowned ? locale::strings[ locale::string::debug_vehicle_owned ].c_str() + vehicle.ctOwner->OwnerName() : "" ).c_str(), - mover.LoadAmount, - mover.LoadType.name.c_str(), - mover.EngineDescription( 0 ).c_str(), - // TODO: put wheel flat reporting in the enginedescription() - std::string( mover.WheelFlat > 0.01 ? " Flat: " + to_string( mover.WheelFlat, 1 ) + " mm" : "" ).c_str(), - update_vehicle_coupler( end::front ).c_str(), - update_vehicle_coupler( end::rear ).c_str() ); + std::snprintf( + m_buffer.data(), m_buffer.size(), + locale::strings[ locale::string::debug_vehicle_nameloadstatuscouplers ].c_str(), + mover.Name.c_str(), + std::string( isowned ? locale::strings[ locale::string::debug_vehicle_owned ].c_str() + vehicle.ctOwner->OwnerName() : "" ).c_str(), + mover.LoadAmount, + mover.LoadType.name.c_str(), + mover.EngineDescription( 0 ).c_str(), + // TODO: put wheel flat reporting in the enginedescription() + std::string( mover.WheelFlat > 0.01 ? " Flat: " + to_string( mover.WheelFlat, 1 ) + " mm" : "" ).c_str(), + update_vehicle_coupler( end::front ).c_str(), + update_vehicle_coupler( end::rear ).c_str() ); - Output.emplace_back( std::string{ m_buffer.data() }, Global.UITextColor ); + Output.emplace_back( std::string{ m_buffer.data() }, Global.UITextColor ); - std::snprintf( - m_buffer.data(), m_buffer.size(), - locale::strings[ locale::string::debug_vehicle_devicespower ].c_str(), - // devices - ( mover.Battery ? 'B' : '.' ), - ( mover.Mains ? 'M' : '.' ), - ( mover.FuseFlag ? '!' : '.' ), - ( mover.PantRearUp ? ( mover.PantRearVolt > 0.0 ? 'O' : 'o' ) : '.' ), - ( mover.PantFrontUp ? ( mover.PantFrontVolt > 0.0 ? 'P' : 'p' ) : '.' ), - ( mover.PantPressLockActive ? '!' : ( mover.PantPressSwitchActive ? '*' : '.' ) ), - ( mover.WaterPump.is_active ? 'W' : ( false == mover.WaterPump.breaker ? '-' : ( mover.WaterPump.is_enabled ? 'w' : '.' ) ) ), - ( true == mover.WaterHeater.is_damaged ? '!' : ( mover.WaterHeater.is_active ? 'H' : ( false == mover.WaterHeater.breaker ? '-' : ( mover.WaterHeater.is_enabled ? 'h' : '.' ) ) ) ), - ( mover.FuelPump.is_active ? 'F' : ( mover.FuelPump.is_enabled ? 'f' : '.' ) ), - ( mover.OilPump.is_active ? 'O' : ( mover.OilPump.is_enabled ? 'o' : '.' ) ), - ( false == mover.ConverterAllowLocal ? '-' : ( mover.ConverterAllow ? ( mover.ConverterFlag ? 'X' : 'x' ) : '.' ) ), - ( mover.ConvOvldFlag ? '!' : '.' ), - ( mover.CompressorFlag ? 'C' : ( false == mover.CompressorAllowLocal ? '-' : ( ( mover.CompressorAllow || mover.CompressorStart == start_t::automatic ) ? 'c' : '.' ) ) ), - ( mover.CompressorGovernorLock ? '!' : '.' ), - std::string( isplayervehicle ? locale::strings[ locale::string::debug_vehicle_radio ] + ( mover.Radio ? std::to_string( m_input.train->RadioChannel() ) : "-" ) : "" ).c_str(), - std::string( isdieselenginepowered ? locale::strings[ locale::string::debug_vehicle_oilpressure ] + to_string( mover.OilPump.pressure, 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( vehicle.DirectionGet() ? ":<<:" : ":>>:" ).c_str(), - std::string( mover.Couplers[ end::rear ].power_high.local ? "" : "-" ).c_str(), - mover.Couplers[ end::rear ].power_high.voltage, - mover.Couplers[ end::rear ].power_high.current ); + std::snprintf( + m_buffer.data(), m_buffer.size(), + locale::strings[ locale::string::debug_vehicle_devicespower ].c_str(), + // devices + ( mover.Battery ? 'B' : '.' ), + ( mover.Mains ? 'M' : '.' ), + ( mover.FuseFlag ? '!' : '.' ), + ( mover.PantRearUp ? ( mover.PantRearVolt > 0.0 ? 'O' : 'o' ) : '.' ), + ( mover.PantFrontUp ? ( mover.PantFrontVolt > 0.0 ? 'P' : 'p' ) : '.' ), + ( mover.PantPressLockActive ? '!' : ( mover.PantPressSwitchActive ? '*' : '.' ) ), + ( mover.WaterPump.is_active ? 'W' : ( false == mover.WaterPump.breaker ? '-' : ( mover.WaterPump.is_enabled ? 'w' : '.' ) ) ), + ( true == mover.WaterHeater.is_damaged ? '!' : ( mover.WaterHeater.is_active ? 'H' : ( false == mover.WaterHeater.breaker ? '-' : ( mover.WaterHeater.is_enabled ? 'h' : '.' ) ) ) ), + ( mover.FuelPump.is_active ? 'F' : ( mover.FuelPump.is_enabled ? 'f' : '.' ) ), + ( mover.OilPump.is_active ? 'O' : ( mover.OilPump.is_enabled ? 'o' : '.' ) ), + ( false == mover.ConverterAllowLocal ? '-' : ( mover.ConverterAllow ? ( mover.ConverterFlag ? 'X' : 'x' ) : '.' ) ), + ( 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 ? locale::strings[ locale::string::debug_vehicle_radio ] + ( mover.Radio ? std::to_string( m_input.train->RadioChannel() ) : "-" ) : "" ).c_str(), + std::string( isdieselenginepowered ? locale::strings[ locale::string::debug_vehicle_oilpressure ] + to_string( mover.OilPump.pressure, 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.is_local ? "" : "-" ).c_str(), + std::string( vehicle.DirectionGet() ? ":<<" : ":>>" ).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 ); - Output.emplace_back( m_buffer.data(), Global.UITextColor ); + Output.emplace_back( m_buffer.data(), Global.UITextColor ); - std::snprintf( - m_buffer.data(), m_buffer.size(), - locale::strings[ locale::string::debug_vehicle_controllersenginerevolutions ].c_str(), - // controllers - mover.MainCtrlPos, - mover.MainCtrlActualPos, - std::string( isdieselinshuntmode ? to_string( mover.AnPos, 2 ) + locale::strings[ locale::string::debug_vehicle_shuntmode ] : std::to_string( mover.ScndCtrlPos ) + "(" + std::to_string( mover.ScndCtrlActualPos ) + ")" ).c_str(), - // engine - mover.EnginePower, - std::abs( mover.TrainType == dt_EZT ? mover.ShowCurrent( 0 ) : mover.Im ), - // revolutions - std::abs( mover.enrot ) * 60, - std::abs( mover.nrot ) * mover.Transmision.Ratio * 60, - mover.RventRot * 60, - std::abs( mover.MotorBlowers[end::front].revolutions ), - std::abs( mover.MotorBlowers[end::rear].revolutions ), - mover.dizel_heat.rpmw, - mover.dizel_heat.rpmw2 ); + std::snprintf( + m_buffer.data(), m_buffer.size(), + locale::strings[ locale::string::debug_vehicle_controllersenginerevolutions ].c_str(), + // controllers + mover.MainCtrlPos, + mover.MainCtrlActualPos, + std::string( isdieselinshuntmode ? to_string( mover.AnPos, 2 ) + locale::strings[ locale::string::debug_vehicle_shuntmode ] : std::to_string( mover.ScndCtrlPos ) + "(" + std::to_string( mover.ScndCtrlActualPos ) + ")" ).c_str(), + // engine + mover.EnginePower, + std::abs( mover.TrainType == dt_EZT ? mover.ShowCurrent( 0 ) : mover.Im ), + // revolutions + std::abs( mover.enrot ) * 60, + std::abs( mover.nrot ) * mover.Transmision.Ratio * 60, + mover.RventRot * 60, + std::abs( mover.MotorBlowers[end::front].revolutions ), + std::abs( mover.MotorBlowers[end::rear].revolutions ), + mover.dizel_heat.rpmw, + mover.dizel_heat.rpmw2 ); - std::string textline { m_buffer.data() }; + std::string textline { m_buffer.data() }; - if( isdieselenginepowered ) { - std::snprintf( - m_buffer.data(), m_buffer.size(), - locale::strings[ locale::string::debug_vehicle_temperatures ].c_str(), - mover.dizel_heat.Ts, - mover.dizel_heat.To, - mover.dizel_heat.temperatura1, - ( mover.WaterCircuitsLink ? '-' : '|' ), - mover.dizel_heat.temperatura2 ); - textline += m_buffer.data(); - } + if( isdieselenginepowered ) { + std::snprintf( + m_buffer.data(), m_buffer.size(), + locale::strings[ locale::string::debug_vehicle_temperatures ].c_str(), + mover.dizel_heat.Ts, + mover.dizel_heat.To, + mover.dizel_heat.temperatura1, + ( mover.WaterCircuitsLink ? '-' : '|' ), + mover.dizel_heat.temperatura2 ); + textline += m_buffer.data(); + } - Output.emplace_back( textline, Global.UITextColor ); + Output.emplace_back( textline, Global.UITextColor ); - std::snprintf( - m_buffer.data(), m_buffer.size(), - locale::strings[ locale::string::debug_vehicle_brakespressures ].c_str(), - // brakes - mover.fBrakeCtrlPos, - mover.LocalBrakePosA, - mover.BrakeOpModeFlag, - update_vehicle_brake().c_str(), - mover.LoadFlag, - // cylinders - mover.BrakePress, - mover.LocBrakePress, - mover.Hamulec->GetBrakeStatus(), - // pipes - mover.PipePress, - mover.BrakeCtrlPos2, - mover.ScndPipePress, - mover.CntrlPipePress, - // tanks - mover.Hamulec->GetBRP(), - mover.Compressor, - mover.Hamulec->GetCRP() ); + std::snprintf( + m_buffer.data(), m_buffer.size(), + locale::strings[ locale::string::debug_vehicle_brakespressures ].c_str(), + // brakes + mover.fBrakeCtrlPos, + mover.LocalBrakePosA, + mover.BrakeOpModeFlag, + update_vehicle_brake().c_str(), + mover.LoadFlag, + // cylinders + mover.BrakePress, + mover.LocBrakePress, + mover.Hamulec->GetBrakeStatus(), + // pipes + mover.PipePress, + mover.BrakeCtrlPos2, + mover.ScndPipePress, + mover.CntrlPipePress, + // tanks + mover.Hamulec->GetBRP(), + mover.Compressor, + mover.Hamulec->GetCRP() ); - textline = m_buffer.data(); + textline = m_buffer.data(); - if( mover.EnginePowerSource.SourceType == TPowerSource::CurrentCollector ) { - std::snprintf( - m_buffer.data(), m_buffer.size(), - locale::strings[ locale::string::debug_vehicle_pantograph ].c_str(), - mover.PantPress, - ( mover.bPantKurek3 ? '-' : '|' ) ); - textline += m_buffer.data(); - } + if( mover.EnginePowerSource.SourceType == TPowerSource::CurrentCollector ) { + std::snprintf( + m_buffer.data(), m_buffer.size(), + locale::strings[ locale::string::debug_vehicle_pantograph ].c_str(), + mover.PantPress, + ( mover.bPantKurek3 ? '-' : '|' ) ); + textline += m_buffer.data(); + } - Output.emplace_back( textline, Global.UITextColor ); + Output.emplace_back( textline, Global.UITextColor ); - if( tprev != simulation::Time.data().wSecond ) { - tprev = simulation::Time.data().wSecond; - Acc = ( mover.Vel - VelPrev ) / 3.6; - VelPrev = mover.Vel; - } + if( tprev != simulation::Time.data().wSecond ) { + tprev = simulation::Time.data().wSecond; + Acc = ( mover.Vel - VelPrev ) / 3.6; + VelPrev = mover.Vel; + } - std::snprintf( - m_buffer.data(), m_buffer.size(), - locale::strings[ locale::string::debug_vehicle_forcesaccelerationvelocityposition ].c_str(), - // forces - mover.Ft * 0.001f * ( mover.ActiveCab ? mover.ActiveCab : vehicle.ctOwner ? vehicle.ctOwner->Controlling()->ActiveCab : 1 ) + 0.001f, - mover.Fb * 0.001f, - mover.Adhesive( mover.RunningTrack.friction ), - ( mover.SlippingWheels ? " (!)" : "" ), - // acceleration - Acc, - mover.AccN + 0.001f, - std::string( std::abs( mover.RunningShape.R ) > 10000.0 ? "~0" : to_string( mover.RunningShape.R, 0 ) ).c_str(), - // velocity - vehicle.GetVelocity(), - mover.DistCounter, - // position - vehicle.GetPosition().x, - vehicle.GetPosition().y, - vehicle.GetPosition().z ); + std::snprintf( + m_buffer.data(), m_buffer.size(), + locale::strings[ locale::string::debug_vehicle_forcesaccelerationvelocityposition ].c_str(), + // forces + mover.Ft * 0.001f * ( mover.ActiveCab ? mover.ActiveCab : vehicle.ctOwner ? vehicle.ctOwner->Controlling()->ActiveCab : 1 ) + 0.001f, + mover.Fb * 0.001f, + mover.Adhesive( mover.RunningTrack.friction ), + ( mover.SlippingWheels ? " (!)" : "" ), + // acceleration + Acc, + mover.AccN + 0.001f, + std::string( std::abs( mover.RunningShape.R ) > 10000.0 ? "~0" : to_string( mover.RunningShape.R, 0 ) ).c_str(), + // velocity + vehicle.GetVelocity(), + mover.DistCounter, + // position + vehicle.GetPosition().x, + vehicle.GetPosition().y, + vehicle.GetPosition().z ); - Output.emplace_back( m_buffer.data(), Global.UITextColor ); + Output.emplace_back( m_buffer.data(), Global.UITextColor ); } std::string debug_panel::update_vehicle_coupler( int const Side ) { - // NOTE: mover and vehicle are guaranteed to be valid by the caller - std::string couplerstatus { locale::strings[ locale::string::debug_vehicle_none ] }; + // NOTE: mover and vehicle are guaranteed to be valid by the caller + std::string couplerstatus { locale::strings[ locale::string::debug_vehicle_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 transcripts_panel::render() { - if( false == is_open ) { return; } - if( true == text_lines.empty() ) { return; } + if( false == is_open ) { return; } + if( true == text_lines.empty() ) { return; } - auto flags = - ImGuiWindowFlags_NoFocusOnAppearing - | ImGuiWindowFlags_NoCollapse - | ( size.x > 0 ? ImGuiWindowFlags_NoResize : 0 ); + auto flags = + ImGuiWindowFlags_NoFocusOnAppearing + | ImGuiWindowFlags_NoCollapse + | ( size.x > 0 ? ImGuiWindowFlags_NoResize : 0 ); - if( size.x > 0 ) { - ImGui::SetNextWindowSize( ImVec2( size.x, size.y ) ); - } - if( size_min.x > 0 ) { - ImGui::SetNextWindowSizeConstraints( ImVec2( size_min.x, size_min.y ), ImVec2( size_max.x, size_max.y ) ); - } - auto const panelname { ( - title.empty() ? - name : - title ) - + "###" + name }; - if( true == ImGui::Begin( panelname.c_str(), &is_open, flags ) ) { - // header section - for( auto const &line : text_lines ) { - ImGui::TextWrapped( "%s", line.data.c_str() ); - } - } - ImGui::End(); + if( size.x > 0 ) { + ImGui::SetNextWindowSize( ImVec2( size.x, size.y ) ); + } + if( size_min.x > 0 ) { + ImGui::SetNextWindowSizeConstraints( ImVec2( size_min.x, size_min.y ), ImVec2( size_max.x, size_max.y ) ); + } + auto const panelname { ( + title.empty() ? + name : + title ) + + "###" + name }; + if( true == ImGui::Begin( panelname.c_str(), &is_open, flags ) ) { + // header section + for( auto const &line : text_lines ) { + ImGui::TextWrapped( "%s", line.data.c_str() ); + } + } + ImGui::End(); } 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 d6bfbdf1..0e86feac 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 9c72227b..5cc7fc49 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 22b586b0..3d4a5174 100644 --- a/translation.cpp +++ b/translation.cpp @@ -70,7 +70,7 @@ init() { "Name: %s%s\nLoad: %.0f %s\nStatus: %s%s\nCouplers:\n front: %s\n rear: %s", ", owned by: ", "none", - "Devices: %c%c%c%c%c%c%c%c%c%c%c%c%c%c%s%s\nPower transfers: %.0f@%.0f%s%s%s%.0f@%.0f", + "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", " radio: ", " oil pressure: ", "Controllers:\n master: %d(%d), secondary: %s\nEngine output: %.1f, current: %.0f\nRevolutions:\n engine: %.0f, motors: %.0f\n engine fans: %.0f, motor fans: %.0f+%.0f, cooling fans: %.0f+%.0f", @@ -185,6 +185,7 @@ init() { "radio channel", "radiostop test", "radiostop", + "radio call 3", "pantograph A", "pantograph B", "pantograph A", @@ -264,7 +265,7 @@ init() { u8"Nazwa: %s%s\nŁadunek: %.0f %s\nStatus: %s%s\nSprzęgi:\n przedni: %s\n tylny: %s", u8", wlaściciel: ", u8"wolny", - u8"Urządzenia: %c%c%c%c%c%c%c%c%c%c%c%c%c%c%s%s\nPrzepływ prądu: %.0f@%.0f%s%s%s%.0f@%.0f", + u8"Urządzenia: %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%s%s\nTransfer prądów: %.0f@%.0f%s%s[%.0f]%s%s%.0f@%.0f", u8" radio: ", u8" cisń. oleju: ", u8"Nastawniki:\n glówny: %d(%d), dodatkowy: %s\nMoc silnika: %.1f, prąd silnika: %.0f\nObroty:\n silnik: %.0f, motory: %.0f, went.silnika: %.0f, went.chłodnicy: %.0f+%.0f", @@ -379,6 +380,7 @@ init() { u8"kanał radia", u8"test radiostopu", u8"radiostop", + u8"zew 3", u8"pantograf A", u8"pantograf B", u8"pantograf A", @@ -498,6 +500,7 @@ init() { "radiochannelnext_sw:", "radiotest_sw:", "radiostop_sw:", + "radiocall3_sw:", "pantfront_sw:", "pantrear_sw:", "pantfrontoff_sw:", diff --git a/translation.h b/translation.h index 94aa0062..d448b695 100644 --- a/translation.h +++ b/translation.h @@ -174,6 +174,7 @@ enum string { cab_radiochannelnext_sw, cab_radiotest_sw, cab_radiostop_sw, + cab_radiocall3_sw, cab_pantfront_sw, cab_pantrear_sw, cab_pantfrontoff_sw, diff --git a/version.h b/version.h index 1fe6e4e2..ce296b32 100644 --- a/version.h +++ b/version.h @@ -1 +1 @@ -#define VERSION_INFO "M7 (gfx-work) 23.05.2019" +#define VERSION_INFO "M7 (gfx-work) 13.07.2019"