From ee1a80d7a8b207f3cac4a653b830478ec3b7663f Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Sat, 28 Dec 2019 02:38:07 +0100 Subject: [PATCH] main breaker readiness cab indicator, compressor preset switch enhancement, compressor logic cleanup, track end detection logic fix --- Driver.cpp | 179 ++++++++++--------- McZapkie/MOVER.h | 1 + McZapkie/Mover.cpp | 432 ++++++++++++++++----------------------------- Train.cpp | 103 +++++------ Train.h | 1 + color.h | 2 +- 6 files changed, 310 insertions(+), 408 deletions(-) diff --git a/Driver.cpp b/Driver.cpp index 86e24496..1aecbc0e 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -746,7 +746,7 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) else if( sSpeedTable[ iLast ].trTrack == tLast ) { // otherwise just mark the last added track as the final one // TODO: investigate exactly how we can wind up not marking the last existing track as actual end - sSpeedTable[ iLast ].iFlags |= spEnd; + sSpeedTable[ iLast ].iFlags |= ( spEnabled | spEnd ); } // to ostatnia pozycja, bo NULL nic nie da, a może się podpiąć obrotnica, czy jakieś transportery return; @@ -919,25 +919,25 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN Drugi parametr ujemny - wskazanie zatrzymania dla krótszych składów (W32). Drugi paramer dodatni - długość peronu (W4). */ - auto L = 0.0; + auto L = 0.0; auto Par1 = sSpeedTable[i].evEvent->input_value(1); auto Par2 = sSpeedTable[i].evEvent->input_value(2); if ((Par2 >= 0) || (fLength < -Par2)) { //użyj tego W4 if (Par1 < 0) { L = -Par1; } - else { + else { //środek L = Par1 - fMinProximityDist - fLength * 0.5; - } + } L = std::max(0.0, std::min(L, std::abs(Par2) - fMinProximityDist - fLength)); sSpeedTable[i].UpdateDistance(L); sSpeedTable[i].bMoved = true; - } - else { + } + else { sSpeedTable[i].iFlags = 0; - } - } + } + } IsAtPassengerStop = ( ( sSpeedTable[ i ].fDist <= passengerstopmaxdistance ) // Ra 2F1I: odległość plus długość pociągu musi być mniejsza od długości @@ -946,9 +946,9 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN // przyjąć odległość fMinProximityDist && ( ( iDrivigFlags & moveStopCloser ) != 0 ? sSpeedTable[ i ].fDist + fLength <= - std::max( - std::abs( sSpeedTable[ i ].evEvent->input_value( 2 ) ), - 2.0 * fMaxProximityDist + fLength ) : // fmaxproximitydist typically equals ~50 m + std::max( + std::abs( sSpeedTable[ i ].evEvent->input_value( 2 ) ), + 2.0 * fMaxProximityDist + fLength ) : // fmaxproximitydist typically equals ~50 m sSpeedTable[ i ].fDist < d_to_next_sem ) ); if( !eSignNext ) { @@ -959,12 +959,12 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN // jeśli jedzie (nie trzeba czekać, aż się drgania wytłumią - drzwi zamykane od 1.0) to będzie zatrzymanie sSpeedTable[ i ].fVelNext = 0; } else if( true == IsAtPassengerStop ) { - // jeśli się zatrzymał przy W4, albo stał w momencie zobaczenia W4 + // jeśli się zatrzymał przy W4, albo stał w momencie zobaczenia W4 if( !AIControllFlag ) { // w razie przełączenia na AI ma nie podciągać do W4, gdy użytkownik zatrzymał za daleko iDrivigFlags &= ~moveStopCloser; } - + if( ( iDrivigFlags & moveDoorOpened ) == 0 ) { // drzwi otwierać jednorazowo iDrivigFlags |= moveDoorOpened; // nie wykonywać drugi raz @@ -1032,8 +1032,8 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN if ( ( true == IsCargoTrain ) || ( true == TrainParams.IsMaintenance() ) || ( TrainParams.IsTimeToGo( simulation::Time.data().wHour, simulation::Time.data().wMinute + simulation::Time.data().wSecond*0.0167 ) ) ) { - // z dalszą akcją czekamy do godziny odjazdu - // cargo trains and passenger trains at maintenance stop don't need to wait + // z dalszą akcją czekamy do godziny odjazdu + // cargo trains and passenger trains at maintenance stop don't need to wait IsAtPassengerStop = false; // przy jakim dystansie (stanie licznika) ma przesunąć na następny postój fLastStopExpDist = mvOccupied->DistCounter + 0.050 + 0.001 * fLength; @@ -1166,8 +1166,8 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN } } if( ( SemNextStopIndex == -1 ) - || ( ( sSpeedTable[ SemNextStopIndex ].fVelNext != 0 ) - && ( sSpeedTable[ i ].fVelNext == 0 ) ) ) { + || ( ( sSpeedTable[ SemNextStopIndex ].fVelNext != 0 ) + && ( sSpeedTable[ i ].fVelNext == 0 ) ) ) { SemNextStopIndex = i; } } @@ -1199,9 +1199,9 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN if( mvOccupied->Vel < 2.0 ) { // stanąć nie musi, ale zwolnić przynajmniej if( ( sSpeedTable[ i ].fDist < fMaxProximityDist ) - && ( Obstacle.distance > 1000 ) ) { - // jest w maksymalnym zasięgu to można go pominąć (wziąć drugą prędkosć) - // as long as there isn't any obstacle in arbitrary view range + && ( Obstacle.distance > 1000 ) ) { + // jest w maksymalnym zasięgu to można go pominąć (wziąć drugą prędkosć) + // as long as there isn't any obstacle in arbitrary view range eSignSkip = sSpeedTable[ i ].evEvent; // jazda na widoczność - skanować możliwość kolizji i nie podjeżdżać zbyt blisko // usunąć flagę po podjechaniu blisko semafora zezwalającego na jazdę @@ -1285,8 +1285,8 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN //sprawdzenie eventów pasywnych przed nami if( ( mvOccupied->CategoryFlag & 1 ) - && ( sSpeedTable[ i ].fDist > Obstacle.distance - 20 ) ) { - // jak sygnał jest dalej niż zawalidroga + && ( sSpeedTable[ i ].fDist > Obstacle.distance - 20 ) ) { + // jak sygnał jest dalej niż zawalidroga v = 0.0; // to może być podany dla tamtego: jechać tak, jakby tam stop był } else @@ -1318,7 +1318,7 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN if( sSpeedTable[ i ].fDist < 0.0 ) { // jeśli przejechany //!!! ustawienie, gdy przejechany jest lepsze niż wcale, ale to jeszcze nie to - VelSignal = v; + VelSignal = v; // to można usunąć (nie mogą być usuwane w skanowaniu) sSpeedTable[ i ].iFlags = 0; } @@ -1330,8 +1330,8 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN if( go == TCommandType::cm_Unknown ) { // jeśli nie było komendy wcześniej - pierwsza się liczy - ustawianie VelSignal if( ( v < 0.0 ) - || ( v >= 1.0 ) ) { - // bo wartość 0.1 służy do hamowania tylko + || ( v >= 1.0 ) ) { + // bo wartość 0.1 służy do hamowania tylko go = TCommandType::cm_SetVelocity; // może odjechać // Ra 2014-06: (VelSignal) nie może być tu ustawiane, bo semafor może być daleko // VelSignal=v; //nie do końca tak, to jest druga prędkość; -1 nie wpisywać... @@ -1346,9 +1346,9 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN if( sSpeedTable[ i ].iFlags & spEvent ) { // jeśli event if( ( sSpeedTable[ i ].evEvent != eSignSkip ) - || ( sSpeedTable[ i ].fVelNext != VelRestricted ) ) { - // ale inny niż ten, na którym minięto S1, chyba że się już zmieniło - // sygnał zezwalający na jazdę wyłącza jazdę na widoczność (po S1 na SBL) + || ( sSpeedTable[ i ].fVelNext != VelRestricted ) ) { + // ale inny niż ten, na którym minięto S1, chyba że się już zmieniło + // sygnał zezwalający na jazdę wyłącza jazdę na widoczność (po S1 na SBL) iDrivigFlags &= ~moveVisibility; // remove restricted speed VelRestricted = -1.0; @@ -1377,66 +1377,85 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN } // jeśli nie ma zawalidrogi } // jeśli event - if (v >= 0.0) { + auto const railwaytrackend { ( true == TestFlag( sSpeedTable[ i ].iFlags, spEnd ) ) && ( mvOccupied->CategoryFlag & 1 ) }; + if( ( v >= 0.0 ) + || ( railwaytrackend ) ) { // pozycje z prędkością -1 można spokojnie pomijać d = sSpeedTable[i].fDist; - if( ( d > 0.0 ) - && ( false == TestFlag( sSpeedTable[ i ].iFlags, spElapsed ) ) ) { - // sygnał lub ograniczenie z przodu (+32=przejechane) - // 2014-02: jeśli stoi, a ma do przejechania kawałek, to niech jedzie - if( ( mvOccupied->Vel < 0.01 ) - && ( true == TestFlag( sSpeedTable[ i ].iFlags, ( spEnabled | spEvent | spPassengerStopPoint ) ) ) - && ( false == IsAtPassengerStop ) ) { - // ma podjechać bliżej - czy na pewno w tym miejscu taki warunek? - a = ( ( d > passengerstopmaxdistance ) || ( ( iDrivigFlags & moveStopCloser ) != 0 ) ? - fAcc : - 0.0 ); - } - else { - // przyspieszenie: ujemne, gdy trzeba hamować - a = ( v * v - mvOccupied->Vel * mvOccupied->Vel ) / ( 25.92 * d ); - if( ( mvOccupied->Vel < v ) - || ( v == 0.0 ) ) { - // if we're going slower than the target velocity and there's enough room for safe stop, speed up - auto const brakingdistance { 1.2 * fBrakeDist * braking_distance_multiplier( v ) }; - if( brakingdistance > 0.0 ) { - // maintain desired acc while we have enough room to brake safely, when close enough start paying attention - // try to make a smooth transition instead of sharp change - a = interpolate( a, AccPreferred, clamp( ( d - brakingdistance ) / brakingdistance, 0.0, 1.0 ) ); + if( v >= 0.0 ) { + if( ( d > 0.0 ) + && ( false == TestFlag( sSpeedTable[ i ].iFlags, spElapsed ) ) ) { + // sygnał lub ograniczenie z przodu (+32=przejechane) + // 2014-02: jeśli stoi, a ma do przejechania kawałek, to niech jedzie + if( ( mvOccupied->Vel < 0.01 ) + && ( true == TestFlag( sSpeedTable[ i ].iFlags, ( spEnabled | spEvent | spPassengerStopPoint ) ) ) + && ( false == IsAtPassengerStop ) ) { + // ma podjechać bliżej - czy na pewno w tym miejscu taki warunek? + a = ( ( d > passengerstopmaxdistance ) || ( ( iDrivigFlags & moveStopCloser ) != 0 ) ? + fAcc : + 0.0 ); + } + else { + // przyspieszenie: ujemne, gdy trzeba hamować + if( v >= 0.0 ) { + a = ( v * v - mvOccupied->Vel * mvOccupied->Vel ) / ( 25.92 * d ); + if( ( mvOccupied->Vel < v ) + || ( v == 0.0 ) ) { + // if we're going slower than the target velocity and there's enough room for safe stop, speed up + auto const brakingdistance { 1.2 * fBrakeDist * braking_distance_multiplier( v ) }; + if( brakingdistance > 0.0 ) { + // maintain desired acc while we have enough room to brake safely, when close enough start paying attention + // try to make a smooth transition instead of sharp change + a = interpolate( a, AccPreferred, clamp( ( d - brakingdistance ) / brakingdistance, 0.0, 1.0 ) ); + } + } + if( ( d < fMinProximityDist ) + && ( v < fVelDes ) ) { + // jak jest już blisko, ograniczenie aktualnej prędkości + fVelDes = v; + } } } - if( ( d < fMinProximityDist ) - && ( v < fVelDes ) ) { + } + else if (sSpeedTable[i].iFlags & spTrack) // jeśli tor + { // tor ogranicza prędkość, dopóki cały skład nie przejedzie, + if( v >= 1.0 ) // EU06 się zawieszało po dojechaniu na koniec toru postojowego + if( d + sSpeedTable[ i ].trTrack->Length() < -fLength ) + if( false == railwaytrackend ) + continue; // zapętlenie, jeśli już wyjechał za ten odcinek + if( v < fVelDes ) { + // ograniczenie aktualnej prędkości aż do wyjechania za ograniczenie + fVelDes = v; + } + if( false == railwaytrackend ) + continue; // i tyle wystarczy + } + else { + // event trzyma tylko jeśli VelNext=0, nawet po przejechaniu (nie powinno dotyczyć samochodów?) + a = (v > 0.0 ? + fAcc : + mvOccupied->Vel < 0.01 ? + 0.0 : // already standing still so no need to bother with brakes + -2.0 ); // ruszanie albo hamowanie + } + } + // track can potentially end, which creates another virtual point of interest with speed limit of 0 at the end of it + // TBD, TODO: when tracing the route create a dedicated table entry for it, to simplify the code? + 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 + auto const stopatendacceleration = ( -1.0 * mvOccupied->Vel * mvOccupied->Vel ) / ( 25.92 * ( d + sSpeedTable[ i ].trTrack->Length() ) ); + if( stopatendacceleration < a ) { + a = stopatendacceleration; + v = 0.0; + d += sSpeedTable[ i ].trTrack->Length(); + if( d < fMinProximityDist ) { // jak jest już blisko, ograniczenie aktualnej prędkości 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, - // d=fLength+d; //zamiana na długość liczoną do przodu - if( v >= 1.0 ) // EU06 się zawieszało po dojechaniu na koniec toru postojowego - if( d + sSpeedTable[ i ].trTrack->Length() < -fLength ) - continue; // zapętlenie, jeśli już wyjechał za ten odcinek - if( v < fVelDes ) { - // ograniczenie aktualnej prędkości aż do wyjechania za ograniczenie - fVelDes = v; - } - // if (v==0.0) fAcc=-0.9; //hamowanie jeśli stop - continue; // i tyle wystarczy - } - else // event trzyma tylko jeśli VelNext=0, nawet po przejechaniu (nie powinno dotyczyć samochodów?) - a = (v > 0.0 ? - fAcc : - mvOccupied->Vel < 0.01 ? - 0.0 : // already standing still so no need to bother with brakes - -2.0 ); // ruszanie albo hamowanie if ((a < fAcc) && (v == std::min(v, fNext))) { // mniejsze przyspieszenie to mniejsza możliwość rozpędzenia się albo konieczność hamowania @@ -5455,7 +5474,7 @@ TController::UpdateSituation(double dt) { ActualProximityDist, Obstacle.distance ); - if( ActualProximityDist <= ( + if( Obstacle.distance <= ( ( mvOccupied->CategoryFlag & 2 ) ? 100.0 : // cars 250.0 ) ) { // others diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 3d159321..7a8ca315 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -1639,6 +1639,7 @@ public: void MainSwitch_( bool const State ); bool ConverterSwitch( bool State, range_t const Notify = range_t::consist );/*! wl/wyl przetwornicy*/ bool CompressorSwitch( bool State, range_t const Notify = range_t::consist );/*! wl/wyl sprezarki*/ + bool ChangeCompressorPreset( int const Change, range_t const Notify = range_t::consist ); /*-funkcje typowe dla lokomotywy elektrycznej*/ void MainsCheck( double const Deltatime ); diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index f928c335..f06fdfb3 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -3186,74 +3186,68 @@ void TMoverParameters::MainSwitch_( bool const State ) { // Q: 20160713 // włączenie / wyłączenie przetwornicy // ************************************************************************************************* -bool TMoverParameters::ConverterSwitch( bool State, range_t const Notify ) -{ - bool CS = false; // Ra: normalnie chyba false? +bool TMoverParameters::ConverterSwitch( bool State, range_t const Notify ) { - if (ConverterAllow != State) - { - ConverterAllow = State; - CS = true; - } - if( ConverterAllow == true ) { - if( Notify != range_t::local ) { - SendCtrlToNext( - "ConverterSwitch", 1, CabActive, - ( Notify == range_t::unit ? - ctrain_controll | ctrain_depot : - ctrain_controll ) ); - } - } - else { - if( Notify != range_t::local ) { - SendCtrlToNext( - "ConverterSwitch", 0, CabActive, - ( Notify == range_t::unit ? - ctrain_controll | ctrain_depot : - ctrain_controll ) ); - } + auto const initialstate { ConverterAllow }; + + ConverterAllow = State; + + if( Notify != range_t::local ) { + SendCtrlToNext( + "ConverterSwitch", + ( State ? 1 : 0 ), + CabActive, + ( Notify == range_t::unit ? + coupling::control | coupling::permanent : + coupling::control ) ); } - return CS; + return ( ConverterAllow != initialstate ); } // ************************************************************************************************* // Q: 20160713 // włączenie / wyłączenie sprężarki // ************************************************************************************************* -bool TMoverParameters::CompressorSwitch( bool State, range_t const Notify ) -{ +bool TMoverParameters::CompressorSwitch( bool State, range_t const Notify ) { + if( CompressorStart != start_t::manual ) { // only pay attention if the compressor can be controlled manually return false; } - bool CS = false; // Ra: normalnie chyba tak? - if ( CompressorAllow != State ) - { - CompressorAllow = State; - CS = true; - } - if( CompressorAllow == true ) { - if( Notify != range_t::local ) { - SendCtrlToNext( - "CompressorSwitch", 1, CabActive, - ( Notify == range_t::unit ? - ctrain_controll | ctrain_depot : - ctrain_controll ) ); - } - } - else { - if( Notify != range_t::local ) { - SendCtrlToNext( - "CompressorSwitch", 0, CabActive, - ( Notify == range_t::unit ? - ctrain_controll | ctrain_depot : - ctrain_controll ) ); - } + auto const initialstate { CompressorAllow }; + + CompressorAllow = State; + + if( Notify != range_t::local ) { + SendCtrlToNext( + "CompressorSwitch", + ( State ? 1 : 0 ), + CabActive, + ( Notify == range_t::unit ? + coupling::control | coupling::permanent : + coupling::control ) ); } - return CS; + return ( CompressorAllow != initialstate ); +} + +bool TMoverParameters::ChangeCompressorPreset( int const State, range_t const Notify ) { + + auto const initialstate { CompressorListPos }; + + CompressorListPos = clamp( State, 0, CompressorListPosNo ); + + if( Notify != range_t::local ) { + SendCtrlToNext( + "CompressorPreset", State, CabActive, + ( Notify == range_t::unit ? + coupling::control | coupling::permanent : + coupling::control ) ); + } + + return ( CompressorListPos != initialstate ); } // ************************************************************************************************* @@ -3700,20 +3694,8 @@ void TMoverParameters::UpdateBrakePressure(double dt) // Q: 20160712 // Obliczanie pracy sprężarki // ************************************************************************************************* -// TODO: clean the method up, a lot of the code is redundant -void TMoverParameters::CompressorCheck(double dt) -{ +void TMoverParameters::CompressorCheck(double dt) { - double MaxCompressorF = CompressorList[TCompressorList::cl_MaxFactor][CompressorListPos] * MaxCompressor; - double MinCompressorF = CompressorList[TCompressorList::cl_MinFactor][CompressorListPos] * MinCompressor; - double CompressorSpeedF = CompressorList[TCompressorList::cl_SpeedFactor][CompressorListPos] * CompressorSpeed; - double AllowFactor = CompressorList[TCompressorList::cl_Allow][CompressorListPos]; - - //checking the impact on the compressor allowance - if (AllowFactor > 0.5) { - CompressorAllow = AllowFactor > 1.5; - } - if( VeselVolume == 0.0 ) { return; } //EmergencyValve @@ -3723,231 +3705,125 @@ void TMoverParameters::CompressorCheck(double dt) CompressedVolume -= dV; } - CompressedVolume = std::max( 0.0, CompressedVolume - dt * AirLeakRate * 0.1 ); // nieszczelności: 0.001=1l/s - if( ( true == CompressorGovernorLock ) - && ( Compressor < MinCompressorF ) ) { - // if the pressure drops below the cut-in level, we can reset compressor governor - // TBD, TODO: don't operate the lock without battery power? - CompressorGovernorLock = false; - } + // assorted operational logic + auto const MaxCompressorF { CompressorList[ TCompressorList::cl_MaxFactor ][ CompressorListPos ] * MaxCompressor }; + auto const MinCompressorF { CompressorList[ TCompressorList::cl_MinFactor ][ CompressorListPos ] * MinCompressor }; + auto const CompressorSpeedF { CompressorList[ TCompressorList::cl_SpeedFactor ][ CompressorListPos ] * CompressorSpeed }; + auto const AllowFactor { CompressorList[ TCompressorList::cl_Allow ][ CompressorListPos ] }; + //checking the impact on the compressor allowance + if (AllowFactor > 0.5) { + CompressorAllow = ( AllowFactor > 1.5 ); + } - if( CompressorPower == 2 ) { - CompressorAllow = ConverterAllow; - } - // TODO: clean up compressor CompressorFlag state code, large parts are cloned and an utter mess - if (MaxCompressorF - MinCompressorF < 0.0001) { - // TODO: investigate purpose of this branch and whether it can be removed as it duplicates later code - if( ( true == CompressorAllow ) - && ( true == CompressorAllowLocal ) - && ( true == Mains ) - && ( MainCtrlPowerPos() > 0 ) ) { - if( Compressor < MaxCompressorF ) { - if( ( EngineType == TEngineType::DieselElectric ) - && ( CompressorPower > 0 ) ) { - CompressedVolume += - CompressorSpeedF - * ( 2.0 * MaxCompressorF - Compressor ) / MaxCompressorF - * ( ( 60.0 * std::abs( enrot ) ) / DElist[ MainCtrlPosNo ].RPM ) - * dt; - } - else { - CompressedVolume += - CompressorSpeedF - * ( 2.0 * MaxCompressorF - Compressor ) / MaxCompressorF - * dt; - TotalCurrent += 0.0015 * PantographVoltage; // tymczasowo tylko obciążenie sprężarki, tak z 5A na sprężarkę - } - } - else { - CompressedVolume = CompressedVolume * 0.8; - SetFlag(SoundFlag, sound::relay | sound::loud); - } + switch( CompressorPower ) { + case 2: { + CompressorAllow = ConverterAllow; + break; } - } - else { - if( CompressorPower == 3 ) { - // experimental: make sure compressor coupled with diesel engine is always ready for work + case 3: { + // HACK: make sure compressor coupled with diesel engine is always ready for work CompressorStart = start_t::automatic; + break; } - if (CompressorFlag) // jeśli sprężarka załączona - { // sprawdzić możliwe warunki wyłączenia sprężarki - if (CompressorPower == 5) // jeśli zasilanie z sąsiedniego członu - { // zasilanie sprężarki w członie ra z członu silnikowego (sprzęg 1) - if( Couplers[ end::rear ].Connected != NULL ) { - CompressorFlag = ( - ( ( Couplers[ end::rear ].Connected->CompressorAllow ) || ( CompressorStart == start_t::automatic ) ) - && ( CompressorAllowLocal ) - && ( Couplers[ end::rear ].Connected->ConverterFlag ) ); - } - else { - // bez tamtego członu nie zadziała - CompressorFlag = false; - } - } - else if (CompressorPower == 4) // jeśli zasilanie z poprzedniego członu - { // zasilanie sprężarki w członie ra z członu silnikowego (sprzęg 1) - if( Couplers[ end::front ].Connected != NULL ) { - CompressorFlag = ( - ( ( Couplers[ end::front ].Connected->CompressorAllow ) || ( CompressorStart == start_t::automatic ) ) - && ( CompressorAllowLocal ) - && ( Couplers[ end::front ].Connected->ConverterFlag ) ); - } - else { - CompressorFlag = false; // bez tamtego członu nie zadziała - } - } - else - CompressorFlag = ( - ( ( CompressorAllow ) || ( CompressorStart == start_t::automatic ) ) - && ( CompressorAllowLocal ) - && ( CompressorPower == 0 ? Mains : - CompressorPower == 3 ? Mains : - ConverterFlag ) ); - - if( Compressor > MaxCompressorF ) { - // wyłącznik ciśnieniowy jest niezależny od sposobu zasilania - // TBD, TODO: don't operate the lock without battery power? - if( CompressorPower == 3 ) { - // if the compressor is powered directly by the engine the lock can't turn it off and instead just changes the output - if( false == CompressorGovernorLock ) { - // emit relay sound when the lock engages (the state change itself is below) and presumably changes where the air goes - SetFlag( SoundFlag, sound::relay | sound::loud ); - } - } - else { - // if the compressor isn't coupled with the engine the lock can control its state freely - CompressorFlag = false; - } - CompressorGovernorLock = true; // prevent manual activation until the pressure goes below cut-in level - } - - if( ( TrainType == dt_ET41 ) - || ( TrainType == dt_ET42 ) ) { - // for these multi-unit engines compressors turn off whenever any of them was affected by the governor - // NOTE: this is crude implementation, TODO: re-implement when a more elegant/flexible system is in place - if( ( Couplers[ 1 ].Connected != nullptr ) - && ( true == TestFlag( Couplers[ 1 ].CouplingFlag, coupling::permanent ) ) ) { - // the first unit isn't allowed to start its compressor until second unit can start its own as well - CompressorFlag &= ( Couplers[ 1 ].Connected->CompressorGovernorLock == false ); - } - if( ( Couplers[ 0 ].Connected != nullptr ) - && ( true == TestFlag( Couplers[ 0 ].CouplingFlag, coupling::permanent ) ) ) { - // the second unit isn't allowed to start its compressor until first unit can start its own as well - CompressorFlag &= ( Couplers[ 0 ].Connected->CompressorGovernorLock == false ); - } - } + default: { + break; } - else { - // jeśli nie załączona - if( ( LastSwitchingTime > CtrlDelay ) - && ( ( Compressor < MinCompressorF ) - || ( ( Compressor < MaxCompressorF ) - && ( false == CompressorGovernorLock ) ) ) ) { - // załączenie przy małym ciśnieniu - // jeśli nie załączona, a ciśnienie za małe - // or if the switch is on and the pressure isn't maxed - if( CompressorPower == 5 ) // jeśli zasilanie z następnego członu - { // zasilanie sprężarki w członie ra z członu silnikowego (sprzęg 1) - if( Couplers[ end::rear ].Connected != NULL ) { - CompressorFlag = ( - ( ( Couplers[ end::rear ].Connected->CompressorAllow ) || ( CompressorStart == start_t::automatic ) ) - && ( CompressorAllowLocal ) - && ( Couplers[ end::rear ].Connected->ConverterFlag ) ); - } - else { - // bez tamtego członu nie zadziała - CompressorFlag = false; - } - } - else if( CompressorPower == 4 ) // jeśli zasilanie z poprzedniego członu - { // zasilanie sprężarki w członie ra z członu silnikowego (sprzęg 1) - if( Couplers[ end::front ].Connected != NULL ) { - CompressorFlag = ( - ( ( Couplers[ end::front ].Connected->CompressorAllow ) || ( CompressorStart == start_t::automatic ) ) - && ( CompressorAllowLocal ) - && ( Couplers[ end::front ].Connected->ConverterFlag ) ); - } - else { - CompressorFlag = false; // bez tamtego członu nie zadziała - } - } - else { - CompressorFlag = ( - ( ( CompressorAllow ) || ( CompressorStart == start_t::automatic ) ) - && ( CompressorAllowLocal ) - && ( CompressorPower == 0 ? Mains : - CompressorPower == 3 ? Mains : - ConverterFlag ) ); - } + } - // NOTE: crude way to enforce simultaneous activation of compressors in multi-unit setups - // TODO: replace this with a more universal activation system down the road - if( ( TrainType == dt_ET41 ) - || ( TrainType == dt_ET42 ) ) { + auto *compressorowner { ( + CompressorPower == 4 ? Couplers[ end::front ].Connected : + CompressorPower == 5 ? Couplers[ end::rear ].Connected : + this ) }; + auto const compressorpower { ( + CompressorPower == 0 ? Mains : + CompressorPower == 3 ? Mains : + ( compressorowner != nullptr ) && ( compressorowner->ConverterFlag ) ) }; + auto const compressorallow { + ( CompressorAllowLocal ) + && ( ( CompressorStart == start_t::automatic ) + || ( ( compressorowner != nullptr ) && ( compressorowner->CompressorAllow ) ) ) }; - if( ( Couplers[1].Connected != nullptr ) - && ( true == TestFlag( Couplers[ 1 ].CouplingFlag, coupling::permanent ) ) ) { - // the first unit isn't allowed to start its compressor until second unit can start its own as well - CompressorFlag &= ( Couplers[ 1 ].Connected->CompressorGovernorLock == false ); - } - if( ( Couplers[ 0 ].Connected != nullptr ) - && ( true == TestFlag( Couplers[ 0 ].CouplingFlag, coupling::permanent ) ) ) { - // the second unit isn't allowed to start its compressor until first unit can start its own as well - CompressorFlag &= ( Couplers[ 0 ].Connected->CompressorGovernorLock == false ); - } - } + auto const pressureistoolow { Compressor < MinCompressorF }; + auto const pressureistoohigh { Compressor > MaxCompressorF }; - if( CompressorFlag ) { - // jeśli została załączona - LastSwitchingTime = 0; // to trzeba ograniczyć ponowne włączenie - } - } + // TBD, TODO: break the lock with no low voltage power? + auto const governorlockispresent { MaxCompressorF - MinCompressorF > 0.0001 }; + CompressorGovernorLock = + ( governorlockispresent ) + && ( false == pressureistoolow ) // unlock if pressure drops below minimal threshold + && ( pressureistoohigh || CompressorGovernorLock ); // lock if pressure goes above maximum threshold + // for these multi-unit engines compressors turn off whenever any of them was affected by the governor + // NOTE: this is crude implementation, limited only to adjacent vehicles + // TODO: re-implement when a more elegant/flexible system is in place + auto const coupledgovernorlock { + ( ( Couplers[ end::rear ].Connected != nullptr ) + && ( true == TestFlag( Couplers[ end::rear ].CouplingFlag, coupling::permanent ) ) + && ( Couplers[ end::rear ].Connected->CompressorGovernorLock ) ) + || ( ( Couplers[ end::front ].Connected != nullptr ) + && ( true == TestFlag( Couplers[ end::front ].CouplingFlag, coupling::permanent ) ) + && ( Couplers[ end::front ].Connected->CompressorGovernorLock ) ) }; + auto const governorlock { CompressorGovernorLock || coupledgovernorlock }; + + auto const compressorflag { CompressorFlag }; + CompressorFlag = + ( compressorpower ) + && ( ( false == governorlock ) || ( CompressorPower == 3 ) ) + && ( ( CompressorFlag ) + || ( ( compressorallow ) && ( LastSwitchingTime > CtrlDelay ) ) ); + + if( ( CompressorFlag ) && ( CompressorFlag != compressorflag ) ) { + // jeśli została załączona to trzeba ograniczyć ponowne włączenie + LastSwitchingTime = 0; + } + + if( false == CompressorFlag ) { return; } + + // working compressor adds air to the air reservoir + switch( CompressorPower ) { + case 3: { + // the compressor is coupled with the diesel engine, engine revolutions affect the output + auto const enginefactor { ( + EngineType == TEngineType::DieselElectric ? ( ( 60.0 * std::abs( enrot ) ) / DElist[ MainCtrlPosNo ].RPM ) : + EngineType == TEngineType::DieselEngine ? ( std::abs( enrot ) / nmax ) : + 1.0 ) }; // shouldn't ever get here but, eh + CompressedVolume += + CompressorSpeedF + * ( 2.0 * MaxCompressorF - Compressor ) / MaxCompressorF + * enginefactor + * dt; + break; } + default: { + // the compressor is a stand-alone device, working at steady pace + CompressedVolume += + CompressorSpeedF + * ( 2.0 * MaxCompressorF - Compressor ) / MaxCompressorF + * dt; + break; + } + } - if( CompressorFlag ) { - // working compressor adds air to the air reservoir - if( CompressorPower == 3 ) { - // the compressor is coupled with the diesel engine, engine revolutions affect the output - if( false == CompressorGovernorLock ) { - auto const enginefactor { ( - EngineType == TEngineType::DieselElectric ? ( ( 60.0 * std::abs( enrot ) ) / DElist[ MainCtrlPosNo ].RPM ) : - EngineType == TEngineType::DieselEngine ? ( std::abs( enrot ) / nmax ) : - 1.0 ) }; // shouldn't ever get here but, eh - CompressedVolume += - CompressorSpeed - * ( 2.0 * MaxCompressorF - Compressor ) / MaxCompressorF - * enginefactor - * dt; - } -/* - else { - // the lock is active, air is being vented out at arbitrary rate - CompressedVolume -= 0.01 * dt; - } -*/ - } - else { - // the compressor is a stand-alone device, working at steady pace - CompressedVolume += - CompressorSpeedF - * ( 2.0 * MaxCompressorF - Compressor ) / MaxCompressorF - * dt; + if( ( pressureistoohigh ) + && ( false == governorlockispresent ) ) { + // vent some air out if there's no governor lock to stop the compressor from exceeding acceptable pressure level + SetFlag( SoundFlag, sound::relay | sound::loud ); + CompressedVolume *= 0.8; + } - if( ( CompressorPower == 5 ) && ( Couplers[ 1 ].Connected != NULL ) ) { - // tymczasowo tylko obciążenie sprężarki, tak z 5A na sprężarkę - Couplers[ 1 ].Connected->TotalCurrent += 0.0015 * Couplers[ 1 ].Connected->PantographVoltage; - } - else if( ( CompressorPower == 4 ) && ( Couplers[ 0 ].Connected != NULL ) ) { - // tymczasowo tylko obciążenie sprężarki, tak z 5A na sprężarkę - Couplers[ 0 ].Connected->TotalCurrent += 0.0015 * Couplers[ 0 ].Connected->PantographVoltage; - } - else { - // tymczasowo tylko obciążenie sprężarki, tak z 5A na sprężarkę - TotalCurrent += 0.0015 * PantographVoltage; - } + // tymczasowo tylko obciążenie sprężarki, tak z 5A na sprężarkę + // TODO: draw power from proper high- or low voltage circuit + switch( CompressorPower ) { + case 3: { + // diesel-powered compressor doesn't draw power + break; + } + default: { + if( compressorowner != nullptr ) { + compressorowner->TotalCurrent += 0.0015 * compressorowner->PantographVoltage; } + break; } } } @@ -11090,6 +10966,10 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C } OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); } + else if( Command == "CompressorPreset" ) { + CompressorListPos = clamp( static_cast( CValue1 ), 0, CompressorListPosNo ); + OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); + } else if (Command == "DoorPermit") { auto const left { CValue2 > 0 ? 1 : 2 }; diff --git a/Train.cpp b/Train.cpp index 24cc858c..3d2c9fdc 100644 --- a/Train.cpp +++ b/Train.cpp @@ -3079,9 +3079,7 @@ void TTrain::OnCommand_compressorenable( TTrain *Train, command_data const &Comm void TTrain::OnCommand_compressordisable( TTrain *Train, command_data const &Command ) { - if( Train->mvControlled->CompressorPower >= 2 ) { - return; - } + if( Train->mvControlled->CompressorPower >= 2 ) { return; } if( Command.action == GLFW_PRESS ) { // visual feedback @@ -3103,12 +3101,8 @@ void TTrain::OnCommand_compressordisable( TTrain *Train, command_data const &Com void TTrain::OnCommand_compressortogglelocal( TTrain *Train, command_data const &Command ) { - if( Train->mvOccupied->CompressorPower >= 2 ) { - return; - } - if( Train->ggCompressorLocalButton.SubModel == nullptr ) { - return; - } + if( Train->mvOccupied->CompressorPower >= 2 ) { return; } + if( Train->ggCompressorLocalButton.SubModel == nullptr ) { return; } if( Command.action == GLFW_PRESS ) { // only reacting to press, so the switch doesn't flip back and forth if key is held down @@ -3131,48 +3125,54 @@ void TTrain::OnCommand_compressortogglelocal( TTrain *Train, command_data const void TTrain::OnCommand_compressorpresetactivatenext(TTrain *Train, command_data const &Command) { - if (Train->mvOccupied->CompressorListPosNo == 0) { - // lights are controlled by preset selector - return; - } - if (Command.action != GLFW_PRESS) { - // one change per key press - return; - } + if (Train->mvOccupied->CompressorListPosNo == 0) { return; } + if( Command.action == GLFW_REPEAT ) { return; } - if ((Train->mvOccupied->CompressorListPos < Train->mvOccupied->CompressorListPosNo) - || (true == Train->mvOccupied->CompressorListWrap)) { - // active light preset is stored as value in range 1-LigthPosNo - Train->mvOccupied->CompressorListPos = ( - Train->mvOccupied->CompressorListPos < Train->mvOccupied->CompressorListPosNo ? - Train->mvOccupied->CompressorListPos + 1 : - 1); // wrap mode + if( Train->ggCompressorListButton.type() == TGaugeType::push ) { + // impulse switch + if( Train->mvOccupied->CompressorListPosNo < Train->mvOccupied->CompressorListDefPos + 1 ) { return; } - // visual feedback - if (Train->ggCompressorListButton.SubModel != nullptr) { - Train->ggCompressorListButton.UpdateValue(Train->mvOccupied->CompressorListPos - 1, Train->dsbSwitch); - } - } + Train->mvOccupied->ChangeCompressorPreset( ( + Command.action == GLFW_PRESS ? + Train->mvOccupied->CompressorListDefPos + 1 : + Train->mvOccupied->CompressorListDefPos ) ); + // visual feedback + Train->ggCompressorListButton.UpdateValue( Train->mvOccupied->CompressorListPos - 1, Train->dsbSwitch ); + } + else { + // multi-state switch + if( Command.action == GLFW_RELEASE ) { return; } + + if( ( Train->mvOccupied->CompressorListPos < Train->mvOccupied->CompressorListPosNo ) + || ( true == Train->mvOccupied->CompressorListWrap ) ) { + // active light preset is stored as value in range 1-LigthPosNo + Train->mvOccupied->ChangeCompressorPreset( ( + Train->mvOccupied->CompressorListPos < Train->mvOccupied->CompressorListPosNo ? + Train->mvOccupied->CompressorListPos + 1 : + 1 ) ); // wrap mode + // visual feedback + Train->ggCompressorListButton.UpdateValue( Train->mvOccupied->CompressorListPos - 1, Train->dsbSwitch ); + } + } } void TTrain::OnCommand_compressorpresetactivateprevious(TTrain *Train, command_data const &Command) { - if (Train->mvOccupied->CompressorListPosNo == 0) { - // lights are controlled by preset selector - return; - } - if (Command.action != GLFW_PRESS) { - // one change per key press - return; - } + if (Train->mvOccupied->CompressorListPosNo == 0) { return; } + if (Command.action != GLFW_PRESS) { return; } // one change per key press + + if( Train->ggCompressorListButton.type() == TGaugeType::push ) { + // impulse switch toggles only between positions 'default' and 'default+1' + return; + } if ((Train->mvOccupied->CompressorListPos > 1) || (true == Train->mvOccupied->CompressorListWrap)) { // active light preset is stored as value in range 1-LigthPosNo - Train->mvOccupied->CompressorListPos = ( + Train->mvOccupied->ChangeCompressorPreset( ( Train->mvOccupied->CompressorListPos > 1 ? Train->mvOccupied->CompressorListPos - 1 : - Train->mvOccupied->CompressorListPosNo); // wrap mode + Train->mvOccupied->CompressorListPosNo) ); // wrap mode // visual feedback if (Train->ggCompressorListButton.SubModel != nullptr) { @@ -3183,18 +3183,12 @@ void TTrain::OnCommand_compressorpresetactivateprevious(TTrain *Train, command_d void TTrain::OnCommand_compressorpresetactivatedefault(TTrain *Train, command_data const &Command) { - if (Train->mvOccupied->CompressorListPosNo == 0) { - // lights are controlled by preset selector - return; - } - if (Command.action != GLFW_PRESS) { - // one change per key press - return; - } + if (Train->mvOccupied->CompressorListPosNo == 0) { return; } + if (Command.action != GLFW_PRESS) { return; } // one change per key press - Train->mvOccupied->CompressorListPos = Train->mvOccupied->CompressorListDefPos; + Train->mvOccupied->ChangeCompressorPreset( Train->mvOccupied->CompressorListDefPos ); - // visual feedback + // visual feedback if (Train->ggCompressorListButton.SubModel != nullptr) { Train->ggCompressorListButton.UpdateValue(Train->mvOccupied->CompressorListPos - 1, Train->dsbSwitch); } @@ -6166,8 +6160,12 @@ bool TTrain::Update( double const Deltatime ) true : false ) ); btLampkaWylSzybkiOff.Turn( + ( ( ( m_linebreakerstate == 2 ) + || ( true == mvControlled->Mains ) ) ? + false : + true ) ); + btLampkaMainBreakerReady.Turn( ( ( ( mvControlled->MainsInitTimeCountdown > 0.0 ) -// || ( fHVoltage == 0.0 ) || ( m_linebreakerstate == 2 ) || ( true == mvControlled->Mains ) ) ? false : @@ -6328,6 +6326,7 @@ bool TTrain::Update( double const Deltatime ) btLampkaSHP.Turn( false ); btLampkaWylSzybki.Turn( false ); btLampkaWylSzybkiOff.Turn( false ); + btLampkaMainBreakerReady.Turn( false ); btLampkaWysRozr.Turn( false ); btLampkaOpory.Turn( false ); btLampkaStyczn.Turn( false ); @@ -6406,7 +6405,7 @@ bool TTrain::Update( double const Deltatime ) btLampkaWylSzybkiB.Turn( mover->Mains ); btLampkaWylSzybkiBOff.Turn( ( false == mover->Mains ) - && ( mover->MainsInitTimeCountdown <= 0.0 ) + /*&& ( mover->MainsInitTimeCountdown <= 0.0 )*/ /*&& ( fHVoltage != 0.0 )*/ ); btLampkaOporyB.Turn( mover->ResistorsFlagCheck() ); @@ -8024,6 +8023,7 @@ void TTrain::clear_cab_controls() btLampkaWylSzybkiOff.Clear(); btLampkaWylSzybkiB.Clear(); btLampkaWylSzybkiBOff.Clear(); + btLampkaMainBreakerReady.Clear(); btLampkaBezoporowa.Clear(); btLampkaBezoporowaB.Clear(); btLampkaMaxSila.Clear(); @@ -8445,6 +8445,7 @@ bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int co { "i-mainbreakerb:", btLampkaWylSzybkiB }, { "i-mainbreakeroff:", btLampkaWylSzybkiOff }, { "i-mainbreakerboff:", btLampkaWylSzybkiBOff }, + { "i-mainbreakerready:", btLampkaMainBreakerReady }, { "i-vent_ovld:", btLampkaNadmWent }, { "i-comp_ovld:", btLampkaNadmSpr }, { "i-resistors:", btLampkaOpory }, diff --git a/Train.h b/Train.h index 0fb6b9df..76cf3767 100644 --- a/Train.h +++ b/Train.h @@ -566,6 +566,7 @@ public: // reszta może by?publiczna TButton btLampkaNadmSil; TButton btLampkaWylSzybki; TButton btLampkaWylSzybkiOff; + TButton btLampkaMainBreakerReady; TButton btLampkaNadmWent; TButton btLampkaNadmSpr; // TODO: implement // yB: drugie lampki dla EP05 i ET42 diff --git a/color.h b/color.h index 4260b1fb..ac084e34 100644 --- a/color.h +++ b/color.h @@ -4,7 +4,7 @@ namespace colors { glm::vec4 const none{ 0.f, 0.f, 0.f, 1.f }; glm::vec4 const white{ 1.f, 1.f, 1.f, 1.f }; -glm::vec4 const shadow{ 0.6f, 0.6f, 0.6f, 1.f }; +glm::vec4 const shadow{ 0.35f, 0.40f, 0.45f, 1.f }; inline glm::vec3