diff --git a/Driver.cpp b/Driver.cpp index 3b552cc6..db67dc91 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -2156,12 +2156,26 @@ bool TController::CheckVehicles(TOrders user) int pantmask = 1; if (iDrivigFlags & movePrimary) { // jeśli jest aktywnie prowadzącym pojazd, może zrobić własny porządek + p = pVehicles[0]; + // establish ownership and vehicle order + while (p) + { + if (p->MoverParameters->EnginePowerSource.SourceType == TPowerSource::CurrentCollector) + { // jeśli pojazd posiada pantograf, to przydzielamy mu maskę, którą będzie informował o jeździe bezprądowej + p->iOverheadMask = pantmask; + pantmask = pantmask << 1; // przesunięcie bitów, max. 32 pojazdy z pantografami w składzie + } + + d = p->DirectionSet(d ? 1 : -1); // zwraca położenie następnego (1=zgodny,0=odwrócony - względem czoła składu) + p->ctOwner = this; // dominator oznacza swoje terytorium + p = p->Next(); // pojazd podłączony od tyłu (licząc od czoła) + } + // with the order established the virtual train manager can do their work p = pVehicles[0]; while (p) { if( p != pVehicle ) { - if( ( ( p->MoverParameters->Couplers[ end::front ].CouplingFlag & ( coupling::control ) ) == 0 ) - && ( ( p->MoverParameters->Couplers[ end::rear ].CouplingFlag & ( coupling::control ) ) == 0 ) ) { + if( false == p->is_connected( pVehicle, coupling::control ) ) { // NOTE: don't set battery in controllable vehicles, let the user/ai do it explicitly // HACK: wagony muszą mieć baterię załączoną do otwarcia drzwi... p->MoverParameters->BatterySwitch( true ); @@ -2177,15 +2191,9 @@ bool TController::CheckVehicles(TOrders user) p->DestinationSet(TrainParams.Relation2, TrainParams.TrainName); // relacja docelowa, jeśli nie było if (AIControllFlag) // jeśli prowadzi komputer p->RaLightsSet(0, 0); // gasimy światła - if (p->MoverParameters->EnginePowerSource.SourceType == TPowerSource::CurrentCollector) - { // jeśli pojazd posiada pantograf, to przydzielamy mu maskę, którą będzie informował o jeździe bezprądowej - p->iOverheadMask = pantmask; - pantmask = pantmask << 1; // przesunięcie bitów, max. 32 pojazdy z pantografami w składzie - } - d = p->DirectionSet(d ? 1 : -1); // zwraca położenie następnego (1=zgodny,0=odwrócony - względem czoła składu) - p->ctOwner = this; // dominator oznacza swoje terytorium p = p->Next(); // pojazd podłączony od tyłu (licząc od czoła) } + if (AIControllFlag) { // jeśli prowadzi komputer if( true == TestFlag( OrderCurrentGet(), Obey_train ) ) { @@ -2562,7 +2570,7 @@ bool TController::PrepareEngine() } } if( ( mvControlling->EnginePowerSource.SourceType != TPowerSource::CurrentCollector ) - || ( std::max( mvControlling->GetTrainsetVoltage(), mvControlling->PantographVoltage ) > mvControlling->EnginePowerSource.CollectorParameters.MinV ) ) { + || ( std::max( mvControlling->GetAnyTrainsetVoltage(), mvControlling->PantographVoltage ) > mvControlling->EnginePowerSource.CollectorParameters.MinV ) ) { mvControlling->MainSwitch( true ); } } diff --git a/DynObj.cpp b/DynObj.cpp index 3dcc55b3..80e0049a 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -2793,7 +2793,7 @@ bool TDynamicObject::Update(double dt, double dt1) || MoverParameters->PantRearUp ) { if( ( MoverParameters->Mains ) - && ( MoverParameters->GetTrainsetVoltage() < 0.1f ) ) { + && ( MoverParameters->GetAnyTrainsetVoltage() < 0.1f ) ) { // Ra 15-01: logować tylko, jeśli WS załączony // yB 16-03: i nie jest to asynchron zasilany z daleka // Ra 15-01: bezwzględne współrzędne pantografu nie są dostępne, @@ -6038,30 +6038,60 @@ int TDynamicObject::DirectionSet(int d) }; // wskaźnik na poprzedni, nawet wirtualny -TDynamicObject * TDynamicObject::PrevAny() { +TDynamicObject * TDynamicObject::PrevAny() const { return MoverParameters->Neighbours[ iDirection ^ 1 ].vehicle; } -TDynamicObject * TDynamicObject::Prev() { +TDynamicObject * TDynamicObject::Prev() const { return ( MoverParameters->Couplers[ iDirection ^ 1 ].CouplingFlag != coupling::faux ? MoverParameters->Neighbours[ iDirection ^ 1 ].vehicle : nullptr );// gdy sprzęg wirtualny, to jakby nic nie było } -TDynamicObject * TDynamicObject::Next() { +TDynamicObject * TDynamicObject::Next() const { return ( MoverParameters->Couplers[ iDirection ].CouplingFlag != coupling::faux ? MoverParameters->Neighbours[ iDirection ].vehicle : nullptr );// gdy sprzęg wirtualny, to jakby nic nie było } -TDynamicObject * TDynamicObject::PrevC(int C) { +TDynamicObject * TDynamicObject::PrevC(int C) const { return ( ( MoverParameters->Couplers[ iDirection ^ 1 ].CouplingFlag & C ) == C ? MoverParameters->Neighbours[ iDirection ^ 1 ].vehicle : nullptr ); // hide neighbour lacking specified connection type } -TDynamicObject * TDynamicObject::NextC(int C) { +TDynamicObject * TDynamicObject::NextC(int C) const { return ( ( MoverParameters->Couplers[ iDirection ].CouplingFlag & C ) == C ? MoverParameters->Neighbours[ iDirection ].vehicle : nullptr ); // hide neighbour lacking specified connection type } + // checks whether there's unbroken connection of specified type to specified vehicle +bool +TDynamicObject::is_connected( TDynamicObject const *Vehicle, coupling const Coupling ) const { + + auto *vehicle { this }; + if( vehicle == Vehicle ) { + // edge case, vehicle is always "connected" with itself + return true; + } + // check ahead, it's more likely the "owner" using this method is located there + while( ( vehicle = vehicle->PrevC( Coupling ) ) != nullptr ) { + if( vehicle == Vehicle ) { + return true; + } + if( vehicle == this ) { + // edge case, looping consist + return false; + } + } + // start anew in the other direction + vehicle = this; + while( ( vehicle = vehicle->NextC( Coupling ) ) != nullptr ) { + if( vehicle == Vehicle ) { + return true; + } + } + // no lack in either direction, give up + return false; +} + // ustalenie następnego (1) albo poprzedniego (0) w składzie bez względu na prawidłowość iDirection TDynamicObject * TDynamicObject::Neighbour(int &dir) { diff --git a/DynObj.h b/DynObj.h index 09889f64..1bd618fb 100644 --- a/DynObj.h +++ b/DynObj.h @@ -484,11 +484,13 @@ private: public: int *iLights; // wskaźnik na bity zapalonych świateł (własne albo innego członu) bool DimHeadlights{ false }; // status of the headlight dimming toggle. NOTE: single toggle for all lights is a simplification. TODO: separate per-light switches - TDynamicObject * PrevAny(); - TDynamicObject * Prev(); - TDynamicObject * Next(); - TDynamicObject * PrevC(int C); - TDynamicObject * NextC(int C); + TDynamicObject * PrevAny() const; + TDynamicObject * Prev() const; + TDynamicObject * Next() const; + TDynamicObject * PrevC(int C) const; + TDynamicObject * NextC(int C) const; + // checks whether there's unbroken connection of specified type to specified vehicle + bool is_connected( TDynamicObject const *Vehicle, coupling const Coupling = coupling::coupler ) const; void SetdMoveLen(double dMoveLen) { MoverParameters->dMoveLen = dMoveLen; } void ResetdMoveLen() { diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 453f783d..1b382294 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -1526,7 +1526,8 @@ public: double ShowEngineRotation(int VehN); // Q ******************************************************************************************* - double GetTrainsetVoltage(void); + double GetTrainsetVoltage( int const Coupling = ( coupling::heating | coupling::highvoltage ) ) const; + double GetAnyTrainsetVoltage() const; bool switch_physics(bool const State); double LocalBrakeRatio(void); double ManualBrakeRatio(void); diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 01534cd0..73af24de 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -733,7 +733,7 @@ void TMoverParameters::UpdatePantVolume(double dt) // opuszczenie pantografów przy niskim ciśnieniu if( TrainType != dt_EZT ) { // pressure switch safety measure -- open the line breaker, unless there's alternate source of traction voltage - if( GetTrainsetVoltage() < EnginePowerSource.CollectorParameters.MinV ) { + if( GetAnyTrainsetVoltage() < EnginePowerSource.CollectorParameters.MinV ) { // TODO: check whether line breaker should be open EMU-wide MainSwitch( false, ( TrainType == dt_EZT ? range_t::unit : range_t::local ) ); } @@ -1472,11 +1472,8 @@ void TMoverParameters::MainsCheck( double const Deltatime ) { // TODO: move other main circuit checks here - if( MainsInitTime == 0.0 ) { return; } + 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 ) { @@ -1491,8 +1488,18 @@ void TMoverParameters::MainsCheck( double const Deltatime ) { break; } } - if( ( localvoltage == 0.0 ) - && ( GetTrainsetVoltage() == 0 ) ) { + auto const maincircuitpowersupply { + ( std::abs( localvoltage ) > 0.1 ) + || ( GetAnyTrainsetVoltage() > 0.1 ) }; + + if( true == maincircuitpowersupply ) { + // all is well + if( MainsInitTimeCountdown > 0.0 ) { + MainsInitTimeCountdown -= Deltatime; + } + } + else { + // no power supply MainsInitTimeCountdown = MainsInitTime; } } @@ -1507,6 +1514,10 @@ void TMoverParameters::PowerCouplersCheck( double const Deltatime ) { localvoltage = HeatingPowerSource.EngineGenerator.voltage - TotalCurrent * 0.02; break; } + case TPowerSource::CurrentCollector: { + localvoltage = PantographVoltage; + break; + } case TPowerSource::Main: { // HACK: main circuit can be fed through couplers, so we explicitly check pantograph supply here localvoltage = ( @@ -1641,7 +1652,7 @@ void TMoverParameters::ConverterCheck( double const Timestep ) { && ( ConverterAllowLocal ) && ( false == PantPressLockActive ) && ( ( Mains ) - || ( GetTrainsetVoltage() > 0 ) ) ) { + || ( GetAnyTrainsetVoltage() > 0.0 ) ) ) { // delay timer can be optionally configured, and is set anew whenever converter goes off if( ConverterStartDelayTimer <= 0.0 ) { ConverterFlag = true; @@ -1697,17 +1708,22 @@ void TMoverParameters::HeatingCheck( double const Timestep ) { } // ...detailed check if we're still here auto const heatingpowerthreshold { 0.1 }; - // start with external power sources + // start with blank slate auto voltage { 0.0 }; - // then try internal ones + // then try specified power source switch( HeatingPowerSource.SourceType ) { case TPowerSource::Generator: { voltage = HeatingPowerSource.EngineGenerator.voltage; break; } + case TPowerSource::CurrentCollector: { + voltage = PantographVoltage; + break; + } case TPowerSource::PowerCable: { if( HeatingPowerSource.PowerType == TPowerType::ElectricPower ) { - voltage = GetTrainsetVoltage(); + // TBD, TODO: limit input voltage to heating coupling type? + voltage = GetAnyTrainsetVoltage(); } break; } @@ -4501,12 +4517,12 @@ void TMoverParameters::ComputeTotalForce(double dt) { if( EngineType == TEngineType::ElectricSeriesMotor ) // potem ulepszyc! pantogtrafy! { // Ra 2014-03: uwzględnienie kierunku jazdy w napięciu na silnikach, a powinien być zdefiniowany nawrotnik - EngineVoltage = - std::max( - GetTrainsetVoltage(), - ( Mains ? - PantographVoltage : - 0 ) ); + EngineVoltage = ( + Mains ? + std::max( + GetAnyTrainsetVoltage(), + PantographVoltage ) : + 0.00 ); if( CabNo == 0 ) { EngineVoltage *= ActiveDir; } @@ -4515,10 +4531,12 @@ void TMoverParameters::ComputeTotalForce(double dt) { } } // bo nie dzialalo else { - EngineVoltage = - std::max( - GetTrainsetVoltage(), - PantographVoltage ); + EngineVoltage = ( + Power > 1.0 ? + std::max( + GetAnyTrainsetVoltage(), + PantographVoltage ) : + 0.0 ); } FTrain = ( @@ -5091,7 +5109,7 @@ double TMoverParameters::TractionForce( double dt ) { case TEngineType::ElectricSeriesMotor: { // update the state of voltage relays - auto const voltage { std::max( GetTrainsetVoltage(), PantographVoltage ) }; + auto const voltage { std::max( GetAnyTrainsetVoltage(), PantographVoltage ) }; NoVoltRelay = ( EnginePowerSource.SourceType != TPowerSource::CurrentCollector ) || ( voltage >= EnginePowerSource.CollectorParameters.MinV ); @@ -5110,8 +5128,8 @@ double TMoverParameters::TractionForce( double dt ) { // TODO: check if we can use instead the code for electricseriesmotor if( ( Mains ) ) { // nie wchodzić w funkcję bez potrzeby - if( ( std::max( GetTrainsetVoltage(), PantographVoltage ) < EnginePowerSource.CollectorParameters.MinV ) - || ( std::max( GetTrainsetVoltage(), PantographVoltage ) > EnginePowerSource.CollectorParameters.MaxV + 200 ) ) { + if( ( std::max( GetAnyTrainsetVoltage(), PantographVoltage ) < EnginePowerSource.CollectorParameters.MinV ) + || ( std::max( GetAnyTrainsetVoltage(), PantographVoltage ) > EnginePowerSource.CollectorParameters.MaxV + 200 ) ) { MainSwitch( false, ( TrainType == dt_EZT ? range_t::unit : range_t::local ) ); // TODO: check whether we need to send this EMU-wide } } @@ -8096,19 +8114,30 @@ std::string TMoverParameters::EngineDescription(int what) const // Q: 20160709 // Funkcja zwracajaca napiecie dla calego skladu, przydatna dla EZT // ************************************************************************************************* -double TMoverParameters::GetTrainsetVoltage(void) +double TMoverParameters::GetTrainsetVoltage( int const Coupling ) const {//ABu: funkcja zwracajaca napiecie dla calego skladu, przydatna dla EZT return std::max( ( ( ( Couplers[end::front].Connected ) - && ( Couplers[ end::front ].Connected->Couplers[ Couplers[ end::front ].ConnectedNr ].power_high.is_live ) ) ? + && ( Couplers[ end::front ].Connected->Couplers[ Couplers[ end::front ].ConnectedNr ].power_high.is_live ) + && ( ( Couplers[ end::front ].CouplingFlag & Coupling ) != 0 ) ) ? Couplers[end::front].Connected->Couplers[ Couplers[end::front].ConnectedNr ].power_high.voltage : 0.0 ), ( ( ( Couplers[end::rear].Connected ) - && ( Couplers[ end::rear ].Connected->Couplers[ Couplers[ end::rear ].ConnectedNr ].power_high.is_live ) ) ? + && ( Couplers[ end::rear ].Connected->Couplers[ Couplers[ end::rear ].ConnectedNr ].power_high.is_live ) + && ( ( Couplers[ end::rear ].CouplingFlag & Coupling ) != 0 ) ) ? Couplers[ end::rear ].Connected->Couplers[ Couplers[ end::rear ].ConnectedNr ].power_high.voltage : 0.0 ) ); } +double TMoverParameters::GetAnyTrainsetVoltage() const { + + return std::max( + GetTrainsetVoltage( coupling::highvoltage ), + ( HeatingAllow ? + GetTrainsetVoltage( coupling::heating ) : + 0.0 ) ); +} + // ************************************************************************************************* // Kasowanie zmiennych pracy fizyki // ************************************************************************************************* @@ -11278,20 +11307,21 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C } /*naladunek/rozladunek*/ // TODO: have these commands leverage load exchange system instead + // TODO: CValue1 defines amount to load/unload else if ( issection( "Load=", Command ) ) { OK = false; // będzie powtarzane aż się załaduje if( ( Vel < 0.1 ) // tolerance margin for small vehicle movements in the consist && ( MaxLoad > 0 ) && ( LoadAmount < MaxLoad * ( 1.0 + OverLoadFactor ) ) - && ( Distance( Loc, CommandIn.Location, Dim, Dim ) < 10 ) ) { // ten peron/rampa + && ( Distance( Loc, CommandIn.Location, Dim, Dim ) < ( CValue2 > 1.0 ? CValue2 : 10.0 ) ) ) { // ten peron/rampa auto const loadname { ToLower( extract_value( "Load", Command ) ) }; if( LoadAmount == 0.f ) { AssignLoad( loadname ); } OK = LoadingDone( - std::min( CValue2, LoadSpeed ), + LoadSpeed, loadname ); // zmienia LoadStatus } else { @@ -11304,10 +11334,10 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C OK = false; // będzie powtarzane aż się rozładuje if( ( Vel < 0.1 ) // tolerance margin for small vehicle movements in the consist && ( LoadAmount > 0 ) // czy jest co rozladowac? - && ( Distance( Loc, CommandIn.Location, Dim, Dim ) < 10 ) ) { // ten peron + && ( Distance( Loc, CommandIn.Location, Dim, Dim ) < ( CValue2 > 1.0 ? CValue2 : 10.0 ) ) ) { // ten peron /*mozna to rozladowac*/ OK = LoadingDone( - -1.f * std::min( CValue2, LoadSpeed ), + -1.f * LoadSpeed, ToLower( extract_value( "UnLoad", Command ) ) ); } else { diff --git a/Train.cpp b/Train.cpp index 607864e8..ce8983e1 100644 --- a/Train.cpp +++ b/Train.cpp @@ -5701,12 +5701,16 @@ bool TTrain::Update( double const Deltatime ) } // Ra 2014-09: napięcia i prądy muszą być ustalone najpierw, bo wysyłane są ewentualnie na PoKeys - if ((mvControlled->EngineType != TEngineType::DieselElectric) - && (mvControlled->EngineType != TEngineType::ElectricInductionMotor)) // Ra 2014-09: czy taki rozdzia? ma sens? - fHVoltage = std::max( mvControlled->PantographVoltage, mvControlled->GetTrainsetVoltage() ); // Winger czy to nie jest zle? + if( ( mvControlled->EngineType != TEngineType::DieselElectric ) + && ( mvControlled->EngineType != TEngineType::ElectricInductionMotor ) ) { // Ra 2014-09: czy taki rozdzia? ma sens? + fHVoltage = std::max( + mvControlled->PantographVoltage, + mvControlled->GetAnyTrainsetVoltage() ); // Winger czy to nie jest zle? + } // *mvControlled->Mains); - else + else { fHVoltage = mvControlled->EngineVoltage; + } if (ShowNextCurrent) { // jeśli pokazywać drugi człon if (mvSecond) diff --git a/version.h b/version.h index cbdc1b73..af0c2a45 100644 --- a/version.h +++ b/version.h @@ -1,5 +1,5 @@ #pragma once #define VERSION_MAJOR 19 -#define VERSION_MINOR 1111 +#define VERSION_MINOR 1115 #define VERSION_REVISION 0