diff --git a/AnimModel.cpp b/AnimModel.cpp index e1b662e7..c01b88b9 100644 --- a/AnimModel.cpp +++ b/AnimModel.cpp @@ -244,24 +244,10 @@ void TAnimContainer::UpdateModel() { } if (fRotateSpeed != 0.0) { - - /* - double dif= fDesiredAngle-fAngle; - double s= fRotateSpeed*sign(dif)*Timer::GetDeltaTime(); - if ((abs(s)-abs(dif))>0) - fAngle= fDesiredAngle; - else - fAngle+= s; - - while (fAngle>360) fAngle-= 360; - while (fAngle<-360) fAngle+= 360; - pSubModel->SetRotate(vRotateAxis,fAngle); - */ - bool anim = false; auto dif = vDesiredAngles - vRotateAngles; double s; - s = fRotateSpeed * sign(dif.x) * Timer::GetDeltaTime(); + s = std::abs( fRotateSpeed ) * sign(dif.x) * Timer::GetDeltaTime(); if (fabs(s) >= fabs(dif.x)) vRotateAngles.x = vDesiredAngles.x; else @@ -269,7 +255,7 @@ void TAnimContainer::UpdateModel() { vRotateAngles.x += s; anim = true; } - s = fRotateSpeed * sign(dif.y) * Timer::GetDeltaTime(); + s = std::abs( fRotateSpeed ) * sign(dif.y) * Timer::GetDeltaTime(); if (fabs(s) >= fabs(dif.y)) vRotateAngles.y = vDesiredAngles.y; else @@ -277,7 +263,7 @@ void TAnimContainer::UpdateModel() { vRotateAngles.y += s; anim = true; } - s = fRotateSpeed * sign(dif.z) * Timer::GetDeltaTime(); + s = std::abs( fRotateSpeed ) * sign(dif.z) * Timer::GetDeltaTime(); if (fabs(s) >= fabs(dif.z)) vRotateAngles.z = vDesiredAngles.z; else @@ -285,22 +271,27 @@ void TAnimContainer::UpdateModel() { vRotateAngles.z += s; anim = true; } - while (vRotateAngles.x >= 360) - vRotateAngles.x -= 360; - while (vRotateAngles.x <= -360) - vRotateAngles.x += 360; - while (vRotateAngles.y >= 360) - vRotateAngles.y -= 360; - while (vRotateAngles.y <= -360) - vRotateAngles.y += 360; - while (vRotateAngles.z >= 360) - vRotateAngles.z -= 360; - while (vRotateAngles.z <= -360) - vRotateAngles.z += 360; - if (vRotateAngles.x == 0.0) - if (vRotateAngles.y == 0.0) - if (vRotateAngles.z == 0.0) - iAnim &= ~1; // kąty są zerowe + // HACK: negative speed allows to work around legacy behaviour, where desired angle > 360 meant permanent rotation + if( fRotateSpeed > 0.0 ) { + while( vRotateAngles.x >= 360 ) + vRotateAngles.x -= 360; + while( vRotateAngles.x <= -360 ) + vRotateAngles.x += 360; + while( vRotateAngles.y >= 360 ) + vRotateAngles.y -= 360; + while( vRotateAngles.y <= -360 ) + vRotateAngles.y += 360; + while( vRotateAngles.z >= 360 ) + vRotateAngles.z -= 360; + while( vRotateAngles.z <= -360 ) + vRotateAngles.z += 360; + } + + if( ( vRotateAngles.x == 0.0 ) + && ( vRotateAngles.y == 0.0 ) + && ( vRotateAngles.z == 0.0 ) ) { + iAnim &= ~1; // kąty są zerowe + } if (!anim) { // nie potrzeba przeliczać już fRotateSpeed = 0.0; diff --git a/Driver.cpp b/Driver.cpp index f005c5cc..590cbee1 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -152,6 +152,7 @@ const double EasyAcceleration = 0.85; //[m/ss] const double HardAcceleration = 9.81; const double PrepareTime = 2.0; //[s] przebłyski świadomości przy odpalaniu bool WriteLogFlag = false; +double const deltalog = 0.05; // przyrost czasu std::string StopReasonTable[] = { // przyczyny zatrzymania ruchu AI @@ -833,7 +834,7 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN double v; // prędkość double d; // droga double d_to_next_sem = 10000.0; //ustaiwamy na pewno dalej niż widzi AI - bool isatpassengerstop { false }; // true if the consist is within acceptable range of w4 post + IsAtPassengerStop = false; TCommandType go = TCommandType::cm_Unknown; eSignNext = NULL; // te flagi są ustawiane tutaj, w razie potrzeby @@ -921,7 +922,7 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN sSpeedTable[i].iFlags = 0; } } - isatpassengerstop = ( + IsAtPassengerStop = ( ( sSpeedTable[ i ].fDist <= passengerstopmaxdistance ) // Ra 2F1I: odległość plus długość pociągu musi być mniejsza od długości // peronu, chyba że pociąg jest dłuższy, to wtedy minimalna. @@ -941,7 +942,7 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN if( mvOccupied->Vel > 0.3 ) { // 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 ) { + } else if( true == IsAtPassengerStop ) { // 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 @@ -1032,7 +1033,7 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN // jeśli są dalsze stacje, czekamy do godziny odjazdu if (TrainParams->IsTimeToGo(simulation::Time.data().wHour, simulation::Time.data().wMinute)) { // z dalszą akcją czekamy do godziny odjazdu - isatpassengerstop = false; + IsAtPassengerStop = false; // przy jakim dystansie (stanie licznika) ma przesunąć na następny postój fLastStopExpDist = mvOccupied->DistCounter + 0.050 + 0.001 * fLength; TrainParams->StationIndexInc(); // przejście do następnej @@ -1373,7 +1374,7 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN // 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 ) ) { + && ( false == IsAtPassengerStop ) ) { // ma podjechać bliżej - czy na pewno w tym miejscu taki warunek? a = ( ( d > passengerstopmaxdistance ) || ( ( iDrivigFlags & moveStopCloser ) != 0 ) ? fAcc : @@ -1454,7 +1455,7 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN } //analiza spisanych z tabelki ograniczeń i nadpisanie aktualnego - if( ( true == isatpassengerstop ) && ( mvOccupied->Vel < 0.01 ) ) { + if( ( true == IsAtPassengerStop ) && ( mvOccupied->Vel < 0.01 ) ) { // if stopped at a valid passenger stop, hold there fVelDes = 0.0; } @@ -1622,6 +1623,11 @@ TController::TController(bool AI, TDynamicObject *NewControll, bool InitPsyche, mvOccupied->TrainType == dt_EZT ? -0.55 : mvOccupied->TrainType == dt_DMU ? -0.45 : -0.2 ); + // HACK: emu with induction motors need to start their braking a bit sooner than the ones with series motors + if( ( mvOccupied->TrainType == dt_EZT ) + && ( mvControlling->EngineType == TEngineType::ElectricInductionMotor ) ) { + fAccThreshold += 0.10; + } } // TrainParams=NewTrainParams; // if (TrainParams) @@ -1670,11 +1676,6 @@ void TController::CloseLog() if (WriteLogFlag) { LogFile.close(); - // if WriteLogFlag) - // CloseFile(AILogFile); - /* append(AIlogFile); - writeln(AILogFile,ElapsedTime5:2,": QUIT"); - close(AILogFile); */ } }; @@ -1737,11 +1738,8 @@ void TController::Activation() mvOccupied->DirectionForward(); // kierunek na 0 while (mvOccupied->ActiveDir > 0) mvOccupied->DirectionBackward(); - if (TestFlag(d->MoverParameters->Couplers[iDirectionOrder < 0 ? 1 : 0].CouplingFlag, - ctrain_controll)) - { - mvControlling->MainSwitch( - false); // dezaktywacja czuwaka, jeśli przejście do innego członu + if (TestFlag(d->MoverParameters->Couplers[iDirectionOrder < 0 ? 1 : 0].CouplingFlag, ctrain_controll)) { + mvControlling->MainSwitch( false); // dezaktywacja czuwaka, jeśli przejście do innego członu mvOccupied->DecLocalBrakeLevel(LocalBrakePosNo); // zwolnienie hamulca w opuszczanym pojeździe // mvOccupied->BrakeLevelSet((mvOccupied->BrakeHandle==FVel6)?4:-2); //odcięcie na // zaworze maszynisty, FVel6 po drugiej stronie nie luzuje @@ -1751,8 +1749,7 @@ void TController::Activation() mvOccupied->ActiveCab = mvOccupied->CabNo; // użytkownik moze zmienić ActiveCab wychodząc mvOccupied->CabDeactivisation(); // tak jest w Train.cpp // przejście AI na drugą stronę EN57, ET41 itp. - while (TestFlag(d->MoverParameters->Couplers[iDirection < 0 ? 1 : 0].CouplingFlag, - ctrain_controll)) + while (TestFlag(d->MoverParameters->Couplers[iDirection < 0 ? 1 : 0].CouplingFlag, ctrain_controll)) { // jeśli pojazd z przodu jest ukrotniony, to przechodzimy do niego d = iDirection * d->DirectionGet() < 0 ? d->Next() : d->Prev(); // przechodzimy do następnego członu @@ -1816,8 +1813,7 @@ void TController::AutoRewident() // · masa (jako suma) -> jest w (fMass) while (d) { // klasyfikacja pojazdów wg BrakeDelays i mocy (licznik) - if (d->MoverParameters->Power < - 1) // - lokomotywa - Power>1 - ale może być nieczynna na końcu... + if (d->MoverParameters->Power < 1) // - lokomotywa - Power>1 - ale może być nieczynna na końcu... if (TestFlag(d->MoverParameters->BrakeDelays, bdelay_R)) ++r; // - wagon pospieszny - jest R else if (TestFlag(d->MoverParameters->BrakeDelays, bdelay_G)) @@ -1951,7 +1947,13 @@ void TController::AutoRewident() 0.25 ); if( mvOccupied->TrainType == dt_EZT ) { - fNominalAccThreshold = std::max( -0.75, -fBrake_a0[ BrakeAccTableSize ] - 8 * fBrake_a1[ BrakeAccTableSize ] ); + if( mvControlling->EngineType == TEngineType::ElectricInductionMotor ) { + // HACK: emu with induction motors need to start their braking a bit sooner than the ones with series motors + fNominalAccThreshold = std::max( -0.60, -fBrake_a0[ BrakeAccTableSize ] - 8 * fBrake_a1[ BrakeAccTableSize ] ); + } + else { + fNominalAccThreshold = std::max( -0.75, -fBrake_a0[ BrakeAccTableSize ] - 8 * fBrake_a1[ BrakeAccTableSize ] ); + } fBrakeReaction = 0.25; } else if( mvOccupied->TrainType == dt_DMU ) { @@ -2083,13 +2085,11 @@ bool TController::CheckVehicles(TOrders user) 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 + { // 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) + 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) } @@ -2109,9 +2109,6 @@ bool TController::CheckVehicles(TOrders user) Lights( frontlights, rearlights ); -#if LOGPRESS == 0 - AutoRewident(); // nastawianie hamulca do jazdy pociągowej -#endif } else if (OrderCurrentGet() & (Shunt | Connect)) { @@ -2144,46 +2141,59 @@ bool TController::CheckVehicles(TOrders user) Lights( 0, light::headlight_right ); } } + // nastawianie hamulca do jazdy pociągowej + if( OrderCurrentGet() & ( Obey_train | Shunt ) ) { + AutoRewident(); + } } - else // Ra 2014-02: lepiej tu niż w pętli obsługującej komendy, bo tam się zmieni informacja - // o składzie - switch (user) // gdy człowiek i gdy nastąpiło połącznie albo rozłączenie - { - case Change_direction: - while (OrderCurrentGet() & (Change_direction)) - JumpToNextOrder(); // zmianę kierunku też można olać, ale zmienić kierunek - // skanowania! + else { // gdy człowiek i gdy nastąpiło połącznie albo rozłączenie + // Ra 2014-02: lepiej tu niż w pętli obsługującej komendy, bo tam się zmieni informacja o składzie + switch (user) { + case Change_direction: { + while (OrderCurrentGet() & (Change_direction)) { + // zmianę kierunku też można olać, ale zmienić kierunek skanowania! + JumpToNextOrder(); + } break; - case Connect: - while (OrderCurrentGet() & (Change_direction)) - JumpToNextOrder(); // zmianę kierunku też można olać, ale zmienić kierunek - // skanowania! - if (OrderCurrentGet() & (Connect)) - { // jeśli miało być łączenie, zakładamy, że jest dobrze (sprawdzić?) + } + case Connect: { + while (OrderCurrentGet() & (Change_direction)) { + // zmianę kierunku też można olać, ale zmienić kierunek skanowania! + JumpToNextOrder(); + } + if (OrderCurrentGet() & (Connect)) { + // jeśli miało być łączenie, zakładamy, że jest dobrze (sprawdzić?) iCoupler = 0; // koniec z doczepianiem iDrivigFlags &= ~moveConnect; // zdjęcie flagi doczepiania JumpToNextOrder(); // wykonanie następnej komendy - if (OrderCurrentGet() & (Change_direction)) - JumpToNextOrder(); // zmianę kierunku też można olać, ale zmienić kierunek - // skanowania! + if (OrderCurrentGet() & (Change_direction)) { + // zmianę kierunku też można olać, ale zmienić kierunek skanowania! + JumpToNextOrder(); + } + } break; - case Disconnect: - while (OrderCurrentGet() & (Change_direction)) - JumpToNextOrder(); // zmianę kierunku też można olać, ale zmienić kierunek - // skanowania! - if (OrderCurrentGet() & (Disconnect)) - { // wypadało by sprawdzić, czy odczepiono wagony w odpowiednim miejscu - // (iVehicleCount) - JumpToNextOrder(); // wykonanie następnej komendy - if (OrderCurrentGet() & (Change_direction)) - JumpToNextOrder(); // zmianę kierunku też można olać, ale zmienić kierunek - // skanowania! - } - break; - default: - break; } + case Disconnect: { + while (OrderCurrentGet() & (Change_direction)) { + // zmianę kierunku też można olać, ale zmienić kierunek skanowania! + JumpToNextOrder(); + } + if (OrderCurrentGet() & (Disconnect)) { + // wypadało by sprawdzić, czy odczepiono wagony w odpowiednim miejscu (iVehicleCount) + JumpToNextOrder(); // wykonanie następnej komendy + if (OrderCurrentGet() & (Change_direction)) { + // zmianę kierunku też można olać, ale zmienić kierunek skanowania! + JumpToNextOrder(); + } + } + break; + } + default: { + break; + } + } // switch + } // Ra 2014-09: tymczasowo prymitywne ustawienie warunku pod kątem SN61 if( ( mvOccupied->TrainType == dt_EZT ) || ( mvOccupied->TrainType == dt_DMU ) @@ -2428,7 +2438,7 @@ bool TController::PrepareEngine() ; // zerowanie napędu mvControlling->ConvOvldFlag = false; // reset nadmiarowego } - else if (false == mvControlling->Mains) { + else if (false == IsLineBreakerClosed) { while (DecSpeed(true)) ; // zerowanie napędu if( mvOccupied->TrainType == dt_SN61 ) { @@ -2441,24 +2451,6 @@ bool TController::PrepareEngine() || ( std::max( mvControlling->GetTrainsetVoltage(), std::abs( mvControlling->RunningTraction.TractionVoltage ) ) > mvControlling->EnginePowerSource.CollectorParameters.MinV ) ) { mvControlling->MainSwitch( true ); } -/* - if (mvControlling->EngineType == DieselEngine) { - // Ra 2014-06: dla SN61 trzeba wrzucić pierwszą pozycję - nie wiem, czy tutaj... - // kiedyś działało... - if (!mvControlling->MainCtrlPos) { - if( mvControlling->RList[ 0 ].R == 0.0 ) { - // gdy na pozycji 0 dawka paliwa jest zerowa, to zgaśnie dlatego trzeba zwiększyć pozycję - mvControlling->IncMainCtrl( 1 ); - } - if( ( !mvControlling->ScndCtrlPos ) // jeśli bieg nie został ustawiony - && ( !mvControlling->MotorParam[ 0 ].AutoSwitch ) // gdy biegi ręczne - && ( mvControlling->MotorParam[ 0 ].mIsat == 0.0 ) ) { // bl,mIsat,fi,mfi - // pierwszy bieg - mvControlling->IncScndCtrl( 1 ); - } - } - } -*/ } else { OK = ( OrderDirectionChange( iDirection, mvOccupied ) == -1 ); @@ -2860,7 +2852,7 @@ bool TController::IncSpeed() auto const sufficienttractionforce { std::abs( mvControlling->Ft ) > ( IsHeavyCargoTrain ? 125 : 100 ) * 1000.0 }; auto const seriesmodefieldshunting { ( mvControlling->ScndCtrlPos > 0 ) && ( mvControlling->RList[ mvControlling->MainCtrlPos ].Bn == 1 ) }; auto const parallelmodefieldshunting { ( mvControlling->ScndCtrlPos > 0 ) && ( mvControlling->RList[ mvControlling->MainCtrlPos ].Bn > 1 ) }; - auto const useseriesmodevoltage { 0.80 * mvControlling->EnginePowerSource.CollectorParameters.MaxV }; + auto const useseriesmodevoltage { mvControlling->EnginePowerSource.CollectorParameters.MaxV * ( IsHeavyCargoTrain ? 0.70 : 0.80 ) }; auto const useseriesmode = ( ( mvControlling->Imax > mvControlling->ImaxLo ) || ( fVoltage < useseriesmodevoltage ) @@ -3204,8 +3196,8 @@ void TController::SpeedCntrl(double DesiredSpeed) else if (mvControlling->ScndCtrlPosNo > 1) { int DesiredPos = 1 + mvControlling->ScndCtrlPosNo * ((DesiredSpeed - 1.0) / mvControlling->Vmax); - while (mvControlling->ScndCtrlPos > DesiredPos) mvControlling->DecScndCtrl(1); - while (mvControlling->ScndCtrlPos < DesiredPos) mvControlling->IncScndCtrl(1); + while( ( mvControlling->ScndCtrlPos > DesiredPos ) && ( true == mvControlling->DecScndCtrl( 1 ) ) ) { ; } // all work is done in the condition loop + while( ( mvControlling->ScndCtrlPos < DesiredPos ) && ( true == mvControlling->IncScndCtrl( 1 ) ) ) { ; } // all work is done in the condition loop } }; @@ -3827,36 +3819,58 @@ TController::UpdateSituation(double dt) { TDynamicObject *p = pVehicles[0]; // pojazd na czole składu while (p) { // sprawdzenie odhamowania wszystkich połączonych pojazdów + auto *vehicle { p->MoverParameters }; if (Ready) { // bo jak coś nie odhamowane, to dalej nie ma co sprawdzać - if (p->MoverParameters->BrakePress >= 0.4) // wg UIC określone sztywno na 0.04 + if (vehicle->BrakePress >= 0.4) // wg UIC określone sztywno na 0.04 { Ready = false; // nie gotowy // Ra: odluźnianie przeładowanych lokomotyw, ciągniętych na zimno - prowizorka... if (AIControllFlag) // skład jak dotąd był wyluzowany { if( ( mvOccupied->BrakeCtrlPos == 0 ) // jest pozycja jazdy - && ( ( p->MoverParameters->Hamulec->GetBrakeStatus() & b_dmg ) == 0 ) // brake isn't broken - && ( p->MoverParameters->PipePress - 5.0 > -0.1 ) // jeśli ciśnienie jak dla jazdy - && ( p->MoverParameters->Hamulec->GetCRP() > p->MoverParameters->PipePress + 0.12 ) ) { // za dużo w zbiorniku + && ( ( vehicle->Hamulec->GetBrakeStatus() & b_dmg ) == 0 ) // brake isn't broken + && ( vehicle->PipePress - 5.0 > -0.1 ) // jeśli ciśnienie jak dla jazdy + && ( vehicle->Hamulec->GetCRP() > vehicle->PipePress + 0.12 ) ) { // za dużo w zbiorniku // indywidualne luzowanko - p->MoverParameters->BrakeReleaser( 1 ); + vehicle->BrakeReleaser( 1 ); } - if (p->MoverParameters->Power > 0.01) // jeśli ma silnik - if (p->MoverParameters->FuseFlag) // wywalony nadmiarowy - Need_TryAgain = true; // reset jak przy wywaleniu nadmiarowego } } } - if (fReady < p->MoverParameters->BrakePress) - fReady = p->MoverParameters->BrakePress; // szukanie najbardziej zahamowanego + if (fReady < vehicle->BrakePress) + fReady = vehicle->BrakePress; // szukanie najbardziej zahamowanego if( ( dy = p->VectorFront().y ) != 0.0 ) { // istotne tylko dla pojazdów na pochyleniu // ciężar razy składowa styczna grawitacji - fAccGravity -= p->MoverParameters->TotalMassxg * dy * ( p->DirectionGet() == iDirection ? 1 : -1 ); + fAccGravity -= vehicle->TotalMassxg * dy * ( p->DirectionGet() == iDirection ? 1 : -1 ); + } + if( ( vehicle->Power > 0.01 ) // jeśli ma silnik + && ( vehicle->FuseFlag ) ) { // wywalony nadmiarowy + Need_TryAgain = true; // reset jak przy wywaleniu nadmiarowego } p = p->Next(); // pojazd podłączony z tyłu (patrząc od czoła) } + + // test state of main switch in all powered vehicles under control + IsLineBreakerClosed = ( mvOccupied->Power > 0.01 ? mvOccupied->Mains : true ); + p = pVehicle; + while( ( true == IsLineBreakerClosed ) + && ( ( p = p->PrevC( coupling::control) ) != nullptr ) ) { + auto const *vehicle { p->MoverParameters }; + if( vehicle->Power > 0.01 ) { + IsLineBreakerClosed = ( IsLineBreakerClosed && vehicle->Mains ); + } + } + p = pVehicle; + while( ( true == IsLineBreakerClosed ) + && ( ( p = p->NextC( coupling::control ) ) != nullptr ) ) { + auto const *vehicle { p->MoverParameters }; + if( vehicle->Power > 0.01 ) { + IsLineBreakerClosed = ( IsLineBreakerClosed && vehicle->Mains ); + } + } + if( iDirection ) { // siłę generują pojazdy na pochyleniu ale działa ona całość składu, więc a=F/m fAccGravity *= iDirection; @@ -4087,6 +4101,12 @@ TController::UpdateSituation(double dt) { // dla nastawienia G koniecznie należy wydłużyć drogę na czas reakcji fBrakeDist += 2 * mvOccupied->Vel; } + if( ( mvOccupied->Vel > 0.05 ) + && ( mvControlling->EngineType == TEngineType::ElectricInductionMotor ) + && ( ( mvControlling->TrainType & dt_EZT ) != 0 ) ) { + // HACK: make the induction motor powered EMUs start braking slightly earlier + fBrakeDist += 10.0; + } /* // take into account effect of gravity (but to stay on safe side of calculations, only downhill) if( fAccGravity > 0.025 ) { @@ -4743,8 +4763,8 @@ TController::UpdateSituation(double dt) { fMinProximityDist : // cars can bunch up tighter fMaxProximityDist ) ); // other vehicle types less so */ - double k = coupler->Connected->Vel; // prędkość pojazdu z przodu (zakładając, - // że jedzie w tę samą stronę!!!) + // prędkość pojazdu z przodu (zakładając, że jedzie w tę samą stronę!!!) + double k = coupler->Connected->Vel; if( k - vel < 5 ) { // porównanie modułów prędkości [km/h] // zatroszczyć się trzeba, jeśli tamten nie jedzie znacząco szybciej @@ -4752,6 +4772,26 @@ TController::UpdateSituation(double dt) { ActualProximityDist, vehicle->fTrackBlock ); + if( ActualProximityDist <= ( + ( mvOccupied->CategoryFlag & 2 ) ? + 100.0 : // cars + 250.0 ) ) { // others + // regardless of driving mode at close distance take precaution measures: + // match the other vehicle's speed or slow down if the other vehicle is stopped + VelDesired = + min_speed( + VelDesired, + std::max( + k, + ( mvOccupied->CategoryFlag & 2 ) ? + 40.0 : // cars + 20.0 ) ); // others + if( vel > VelDesired + fVelPlus ) { + // if going too fast force some prompt braking + AccPreferred = std::min( -0.65, AccPreferred ); + } + } + double const distance = vehicle->fTrackBlock - fMaxProximityDist - ( fBrakeDist * 1.15 ); // odległość bezpieczna zależy od prędkości if( distance < 0.0 ) { // jeśli odległość jest zbyt mała @@ -4801,7 +4841,7 @@ TController::UpdateSituation(double dt) { } } ReactionTime = ( - vel != 0.0 ? + mvOccupied->Vel > 0.01 ? 0.1 : // orientuj się, bo jest goraco 2.0 ); // we're already standing still, so take it easy } @@ -5219,10 +5259,10 @@ TController::UpdateSituation(double dt) { return; // ...and don't touch any other controls } - if (mvControlling->ConvOvldFlag || - !mvControlling->Mains) // WS może wywalić z powodu błędu w drutach - { // wywalił bezpiecznik nadmiarowy przetwornicy - PrepareEngine(); // próba ponownego załączenia + if( ( true == mvControlling->ConvOvldFlag ) // wywalił bezpiecznik nadmiarowy przetwornicy + || ( false == IsLineBreakerClosed ) ) { // WS może wywalić z powodu błędu w drutach + // próba ponownego załączenia + PrepareEngine(); } // włączanie bezpiecznika if ((mvControlling->EngineType == TEngineType::ElectricSeriesMotor) || @@ -5784,7 +5824,7 @@ void TController::OrdersInit(double fVel) // Ale mozna by je zapodac ze scenerii }; -std::string TController::StopReasonText() +std::string TController::StopReasonText() const { // informacja tekstowa o przyczynie zatrzymania if (eStopReason != 7) // zawalidroga będzie inaczej return StopReasonTable[eStopReason]; diff --git a/Driver.h b/Driver.h index c0f50fbf..9140a899 100644 --- a/Driver.h +++ b/Driver.h @@ -129,12 +129,7 @@ class TSpeedPos double fDist{ 0.0 }; // aktualna odległość (ujemna gdy minięte) double fVelNext{ -1.0 }; // prędkość obowiązująca od tego miejsca double fSectionVelocityDist{ 0.0 }; // długość ograniczenia prędkości - // double fAcc; int iFlags{ spNone }; // flagi typu wpisu do tabelki - // 1=istotny,2=tor,4=odwrotnie,8-zwrotnica (może się zmienić),16-stan - // zwrotnicy,32-minięty,64=koniec,128=łuk - // 0x100=event,0x200=manewrowa,0x400=przystanek,0x800=SBL,0x1000=wysłana komenda,0x2000=W5 - // 0x4000=semafor,0x10000=zatkanie bool bMoved{ false }; // czy przesunięty (dotyczy punktu zatrzymania w peronie) Math3D::vector3 vPos; // współrzędne XYZ do liczenia odległości struct @@ -174,88 +169,76 @@ static const int BrakeAccTableSize = 20; //---------------------------------------------------------------------------- class TController { + // TBD: few authorized inspectors, or bunch of public getters? + friend class TTrain; + friend class drivingaid_panel; + friend class timetable_panel; + friend class debug_panel; + friend class whois_event; - private: // obsługa tabelki prędkości (musi mieć możliwość odhaczania stacji w rozkładzie) - int iLast{ 0 }; // ostatnia wypełniona pozycja w tabeli sSpeedTable; - double fLastVel = 0.0; // prędkość na poprzednio sprawdzonym torze - TTrack *tLast = nullptr; // ostatni analizowany tor - basic_event *eSignSkip = nullptr; // można pominąć ten SBL po zatrzymaniu - std::size_t SemNextIndex{ std::size_t(-1) }; - std::size_t SemNextStopIndex{ std::size_t( -1 ) }; - double dMoveLen = 0.0; // odległość przejechana od ostatniego sprawdzenia tabelki - // parametry aktualnego składu - double fLength = 0.0; // długość składu (do wyciągania z ograniczeń) - double fMass = 0.0; // całkowita masa do liczenia stycznej składowej grawitacji public: - double fAccGravity = 0.0; // przyspieszenie składowej stycznej grawitacji - basic_event *eSignNext = nullptr; // sygnał zmieniający prędkość, do pokazania na [F2] - std::string asNextStop; // nazwa następnego punktu zatrzymania wg rozkładu - int iStationStart = 0; // numer pierwszej stacji pokazywanej na podglądzie rozkładu - // parametry sterowania pojazdem (stan, hamowanie) - private: - double fShuntVelocity = 40.0; // maksymalna prędkość manewrowania, zależy m.in. od składu // domyślna prędkość manewrowa - int iVehicles = 0; // ilość pojazdów w składzie - int iEngineActive = 0; // ABu: Czy silnik byl juz zalaczony; Ra: postęp w załączaniu - bool Psyche = false; - int iDrivigFlags = // flagi bitowe ruchu - moveStopPoint | // podjedź do W4 możliwie blisko - moveStopHere | // nie podjeżdżaj do semafora, jeśli droga nie jest wolna - moveStartHorn; // podaj sygnał po podaniu wolnej drogi - double fDriverBraking = 0.0; // po pomnożeniu przez v^2 [km/h] daje ~drogę hamowania [m] - double fDriverDist = 0.0; // dopuszczalna odległość podjechania do przeszkody - double fVelMax = -1.0; // maksymalna prędkość składu (sprawdzany każdy pojazd) - public: - double fBrakeDist = 0.0; // przybliżona droga hamowania - double BrakeAccFactor() const; - double fBrakeReaction = 1.0; //opóźnienie zadziałania hamulca - czas w s / (km/h) - double fNominalAccThreshold = 0.0; // nominalny próg opóźnienia dla zadziałania hamulca - double fAccThreshold = 0.0; // aktualny próg opóźnienia dla zadziałania hamulca - double AbsAccS_pub = 0.0; // próg opóźnienia dla zadziałania hamulca - // dla fBrake_aX: - // indeks [0] - wartości odpowiednie dla aktualnej prędkości - // a potem jest 20 wartości dla różnych prędkości zmieniających się co 5 % Vmax pojazdu obsadzonego - double fBrake_a0[BrakeAccTableSize+1] = { 0.0 }; // opóźnienia hamowania przy ustawieniu zaworu maszynisty w pozycji 1.0 - double fBrake_a1[BrakeAccTableSize+1] = { 0.0 }; // przyrost opóźnienia hamowania po przestawieniu zaworu maszynisty o 0,25 pozycji - double BrakingInitialLevel{ 1.0 }; - double BrakingLevelIncrease{ 0.25 }; - bool IsCargoTrain{ false }; - bool IsHeavyCargoTrain{ false }; - double fLastStopExpDist = -1.0; // odległość wygasania ostateniego przystanku - double ReactionTime = 0.0; // czas reakcji Ra: czego i na co? świadomości AI - double fBrakeTime = 0.0; // wpisana wartość jest zmniejszana do 0, gdy ujemna należy zmienić nastawę hamulca - double BrakeChargingCooldown {}; // prevents the ai from trying to charge the train brake too frequently - double fReady = 0.0; // poziom odhamowania wagonów - bool Ready = false; // ABu: stan gotowosci do odjazdu - sprawdzenie odhamowania wagonow -private: - double LastUpdatedTime = 0.0; // czas od ostatniego logu - double ElapsedTime = 0.0; // czas od poczatku logu - double deltalog = 0.05; // przyrost czasu - double LastReactionTime = 0.0; - double fActionTime = 0.0; // czas używany przy regulacji prędkości i zamykaniu drzwi - double m_radiocontroltime{ 0.0 }; // timer used to control speed of radio operations - TAction eAction { TAction::actUnknown }; // aktualny stan - public: + TController( bool AI, TDynamicObject *NewControll, bool InitPsyche, bool primary = true ); + ~TController(); + +// ai operations logic +// methods +public: + void UpdateSituation(double dt); // uruchamiac przynajmniej raz na sekundę + void MoveTo(TDynamicObject *to); + void TakeControl(bool yes); + inline + bool Primary() const { + return ( ( iDrivigFlags & movePrimary ) != 0 ); }; + inline + TMoverParameters const *Controlling() const { + return mvControlling; } + void DirectionInitial(); + inline + int Direction() const { + return iDirection; } inline TAction GetAction() { return eAction; } +private: + void Activation(); // umieszczenie obsady w odpowiednim członie + void ControllingSet(); // znajduje człon do sterowania + void SetDriverPsyche(); + bool IncBrake(); + bool DecBrake(); + bool IncSpeed(); + bool DecSpeed(bool force = false); + void SpeedSet(); + void SpeedCntrl(double DesiredSpeed); + double ESMVelocity(bool Main); + bool UpdateHeating(); + // uaktualnia informacje o prędkości + void SetVelocity(double NewVel, double NewVelNext, TStopReason r = stopNone); + int CheckDirection(); + void WaitingSet(double Seconds); + void DirectionForward(bool forward); + int OrderDirectionChange(int newdir, TMoverParameters *Vehicle); + void Lights(int head, int rear); + std::string StopReasonText() const; + double BrakeAccFactor() const; + // modifies brake distance for low target speeds, to ease braking rate in such situations + float + braking_distance_multiplier( float const Targetvelocity ) const; + inline + int DrivigFlags() const { + return iDrivigFlags; }; +// members +public: bool AIControllFlag = false; // rzeczywisty/wirtualny maszynista -/* - int iRouteWanted = 3; // oczekiwany kierunek jazdy (0-stop,1-lewo,2-prawo,3-prosto) np. odpala migacz lub czeka na stan zwrotnicy -*/ - private: + 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 +private: + bool Psyche = false; + int HelperState = 0; //stan pomocnika maszynisty TDynamicObject *pVehicle = nullptr; // pojazd w którym siedzi sterujący - TDynamicObject *pVehicles[2]; // skrajne pojazdy w składzie (niekoniecznie bezpośrednio sterowane) TMoverParameters *mvControlling = nullptr; // jakim pojazdem steruje (może silnikowym w EZT) TMoverParameters *mvOccupied = nullptr; // jakim pojazdem hamuje - Mtable::TTrainParameters *TrainParams = nullptr; // rozkład jazdy zawsze jest, nawet jeśli pusty - int iRadioChannel = 1; // numer aktualnego kanału radiowego - int iGuardRadio = 0; // numer kanału radiowego kierownika (0, gdy nie używa radia) - sound_source tsGuardSignal { sound_placement::internal }; + std::string VehicleName; std::array m_lighthints { -1 }; // suggested light patterns - public: - int HelperState = 0; //stan pomocnika maszynisty double AccPreferred = 0.0; // preferowane przyspieszenie (wg psychiki kierującego, zmniejszana przy wykryciu kolizji) double AccDesired = AccPreferred; // przyspieszenie, jakie ma utrzymywać (<0:nie przyspieszaj,<-0.1:hamuj) double VelDesired = 0.0; // predkość, z jaką ma jechać, wynikająca z analizy tableki; <=VelSignal @@ -269,18 +252,8 @@ private: double VelRoad = -1.0; // aktualna prędkość drogowa (ze znaku W27) (PutValues albo komendą) // prędkość drogowa bez ograniczenia double VelNext = 120.0; // prędkość, jaka ma być po przejechaniu długości ProximityDist double VelRestricted = -1.0; // speed of travel after passing a permissive signal at stop - private: double FirstSemaphorDist = 10000.0; // odległość do pierwszego znalezionego semafora - public: double ActualProximityDist = 1.0; // odległość brana pod uwagę przy wyliczaniu prędkości i przyspieszenia - private: - Math3D::vector3 vCommandLocation; // polozenie wskaznika, sygnalizatora lub innego obiektu do ktorego - // odnosi sie komenda - TOrders OrderList[maxorders]; // lista rozkazów - int OrderPos = 0, - OrderTop = 0; // rozkaz aktualny oraz wolne miejsce do wstawiania nowych - std::ofstream LogFile; // zapis parametrow fizycznych - std::ofstream AILogFile; // log AI int iDirection = 0; // kierunek jazdy względem sprzęgów pojazdu, w którym siedzi AI (1=przód,-1=tył) int iDirectionOrder = 0; //żadany kierunek jazdy (służy do zmiany kierunku) int iVehicleCount = -2; // wartość neutralna // ilość pojazdów do odłączenia albo zabrania ze składu (-1=wszystkie) @@ -288,146 +261,187 @@ private: int iDriverFailCount = 0; // licznik błędów AI bool Need_TryAgain = false; // true, jeśli druga pozycja w elektryku nie załapała bool Need_BrakeRelease = true; - - public: + bool IsAtPassengerStop{ false }; // true if the consist is within acceptable range of w4 post double fMinProximityDist = 30.0; // stawanie między 30 a 60 m przed przeszkodą // minimalna oległość do przeszkody, jaką należy zachować double fOverhead1 = 3000.0; // informacja o napięciu w sieci trakcyjnej (0=brak drutu, zatrzymaj!) double fOverhead2 = -1.0; // informacja o sposobie jazdy (-1=normalnie, 0=bez prądu, >0=z opuszczonym i ograniczeniem prędkości) - 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 fVoltage = 0.0; // uśrednione napięcie sieci: przy spadku poniżej wartości minimalnej opóźnić rozruch o losowy czas - private: double fMaxProximityDist = 50.0; // stawanie między 30 a 60 m przed przeszkodą // akceptowalna odległość stanięcia przed przeszkodą TStopReason eStopReason = stopSleep; // powód zatrzymania przy ustawieniu zerowej prędkości // na początku śpi - std::string VehicleName; double fVelPlus = 0.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania double fVelMinus = 0.0; // margines obniżenia prędkości, powodujący załączenie napędu double fWarningDuration = 0.0; // ile czasu jeszcze trąbić double WaitingTime = 0.0; // zliczany czas oczekiwania do samoistnego ruszenia double WaitingExpireTime = 31.0; // tyle ma czekać, zanim się ruszy // maksymlany czas oczekiwania do samoistnego ruszenia double IdleTime {}; // keeps track of time spent at a stop - public: double fStopTime = 0.0; // czas postoju przed dalszą jazdą (np. na przystanku) + double fShuntVelocity = 40.0; // maksymalna prędkość manewrowania, zależy m.in. od składu // domyślna prędkość manewrowa + int iDrivigFlags = // flagi bitowe ruchu + moveStopPoint | // podjedź do W4 możliwie blisko + moveStopHere | // nie podjeżdżaj do semafora, jeśli droga nie jest wolna + moveStartHorn; // podaj sygnał po podaniu wolnej drogi + double fDriverBraking = 0.0; // po pomnożeniu przez v^2 [km/h] daje ~drogę hamowania [m] + double fDriverDist = 0.0; // dopuszczalna odległość podjechania do przeszkody + double fVelMax = -1.0; // maksymalna prędkość składu (sprawdzany każdy pojazd) + double fBrakeDist = 0.0; // przybliżona droga hamowania + double fBrakeReaction = 1.0; //opóźnienie zadziałania hamulca - czas w s / (km/h) + double fNominalAccThreshold = 0.0; // nominalny próg opóźnienia dla zadziałania hamulca + double fAccThreshold = 0.0; // aktualny próg opóźnienia dla zadziałania hamulca + double AbsAccS_pub = 0.0; // próg opóźnienia dla zadziałania hamulca + // dla fBrake_aX: + // indeks [0] - wartości odpowiednie dla aktualnej prędkości + // a potem jest 20 wartości dla różnych prędkości zmieniających się co 5 % Vmax pojazdu obsadzonego + double fBrake_a0[BrakeAccTableSize+1] = { 0.0 }; // opóźnienia hamowania przy ustawieniu zaworu maszynisty w pozycji 1.0 + double fBrake_a1[BrakeAccTableSize+1] = { 0.0 }; // przyrost opóźnienia hamowania po przestawieniu zaworu maszynisty o 0,25 pozycji + double BrakingInitialLevel{ 1.0 }; + double BrakingLevelIncrease{ 0.25 }; + double ReactionTime = 0.0; // czas reakcji Ra: czego i na co? świadomości AI + double fBrakeTime = 0.0; // wpisana wartość jest zmniejszana do 0, gdy ujemna należy zmienić nastawę hamulca + double BrakeChargingCooldown {}; // prevents the ai from trying to charge the train brake too frequently + double LastReactionTime = 0.0; + double fActionTime = 0.0; // czas używany przy regulacji prędkości i zamykaniu drzwi + double m_radiocontroltime{ 0.0 }; // timer used to control speed of radio operations + TAction eAction { TAction::actUnknown }; // aktualny stan - private: //---//---//---//---// koniec zmiennych, poniżej metody //---//---//---//---// - void SetDriverPsyche(); - bool PrepareEngine(); - bool ReleaseEngine(); - bool IncBrake(); - bool DecBrake(); - bool IncSpeed(); - bool DecSpeed(bool force = false); - void SpeedSet(); - void SpeedCntrl(double DesiredSpeed); - void Doors(bool const Open, int const Side = 0); - // returns true if any vehicle in the consist has an open door - bool doors_open() const; - void RecognizeCommand(); // odczytuje komende przekazana lokomotywie - void Activation(); // umieszczenie obsady w odpowiednim członie - void ControllingSet(); // znajduje człon do sterowania - void AutoRewident(); // ustawia hamulce w składzie - double ESMVelocity(bool Main); - public: +// orders +// methods +public: void PutCommand(std::string NewCommand, double NewValue1, double NewValue2, const TLocation &NewLocation, TStopReason reason = stopComm); bool PutCommand( std::string NewCommand, double NewValue1, double NewValue2, glm::dvec3 const *NewLocation, TStopReason reason = stopComm ); - void UpdateSituation(double dt); // uruchamiac przynajmniej raz na sekundę - bool UpdateHeating(); - // procedury dotyczace rozkazow dla maszynisty - // uaktualnia informacje o prędkości - void SetVelocity(double NewVel, double NewVelNext, TStopReason r = stopNone); - public: +private: + void RecognizeCommand(); // odczytuje komende przekazana lokomotywie void JumpToNextOrder(); void JumpToFirstOrder(); void OrderPush(TOrders NewOrder); void OrderNext(TOrders NewOrder); inline TOrders OrderCurrentGet(); inline TOrders OrderNextGet(); - bool CheckVehicles(TOrders user = Wait_for_orders); - int CheckDirection(); - - private: - void CloseLog(); void OrderCheck(); - - public: void OrdersInit(double fVel); void OrdersClear(); void OrdersDump(); - TController( bool AI, TDynamicObject *NewControll, bool InitPsyche, bool primary = true ); std::string OrderCurrent() const; - void WaitingSet(double Seconds); - - private: std::string Order2Str(TOrders Order) const; - void DirectionForward(bool forward); - int OrderDirectionChange(int newdir, TMoverParameters *Vehicle); - void Lights(int head, int rear); +// members + Math3D::vector3 vCommandLocation; // polozenie wskaznika, sygnalizatora lub innego obiektu do ktorego odnosi sie komenda // NOTE: not used + TOrders OrderList[ maxorders ]; // lista rozkazów + int OrderPos = 0, + OrderTop = 0; // rozkaz aktualny oraz wolne miejsce do wstawiania nowych + +// scantable +// methods +public: + int CrossRoute(TTrack *tr); + inline void MoveDistanceAdd( double distance ) { + dMoveLen += distance * iDirection; } //jak jedzie do tyłu to trzeba uwzględniać, że distance jest ujemna +private: // Ra: metody obsługujące skanowanie toru - std::vector CheckTrackEvent(TTrack *Track, double const fDirection ) const; + std::vector CheckTrackEvent( TTrack *Track, double const fDirection ) const; bool TableAddNew(); - bool TableNotFound(basic_event const *Event) const; - void TableTraceRoute(double fDistance, TDynamicObject *pVehicle); - void TableCheck(double fDistance); - TCommandType TableUpdate(double &fVelDes, double &fDist, double &fNext, double &fAcc); - // modifies brake distance for low target speeds, to ease braking rate in such situations - float - braking_distance_multiplier( float const Targetvelocity ) const; + bool TableNotFound( basic_event const *Event ) const; + void TableTraceRoute( double fDistance, TDynamicObject *pVehicle ); + void TableCheck( double fDistance ); + TCommandType TableUpdate( double &fVelDes, double &fDist, double &fNext, double &fAcc ); + // returns most recently calculated distance to potential obstacle ahead + double TrackBlock() const; void TablePurger(); void TableSort(); inline double MoveDistanceGet() const { - return dMoveLen; } + return dMoveLen; + } inline void MoveDistanceReset() { - dMoveLen = 0.0; } - public: - inline void MoveDistanceAdd(double distance) { - dMoveLen += distance * iDirection; //jak jedzie do tyłu to trzeba uwzględniać, że distance jest ujemna + dMoveLen = 0.0; } std::size_t TableSize() const { return sSpeedTable.size(); } void TableClear(); int TableDirection() { return iTableDirection; } - - private: // Ra: stare funkcje skanujące, używane do szukania sygnalizatora z tyłu - bool BackwardTrackBusy(TTrack *Track); - basic_event *CheckTrackEventBackward(double fDirection, TTrack *Track); - TTrack *BackwardTraceRoute(double &fDistance, double &fDirection, TTrack *Track, basic_event *&Event); + // Ra: stare funkcje skanujące, używane do szukania sygnalizatora z tyłu + bool BackwardTrackBusy( TTrack *Track ); + basic_event *CheckTrackEventBackward( double fDirection, TTrack *Track ); + TTrack *BackwardTraceRoute( double &fDistance, double &fDirection, TTrack *Track, basic_event *&Event ); void SetProximityVelocity( double dist, double vel, glm::dvec3 const *pos ); TCommandType BackwardScan(); - - public: - void PhysicsLog(); - std::string StopReasonText(); - ~TController(); - void TakeControl(bool yes); - Mtable::TTrainParameters const * TrainTimetable() const; - std::string TrainName() const; - std::string Relation() const; - int StationCount() const; - int StationIndex() const; - bool IsStop() const; - std::string NextStop() const; - inline - bool Primary() const { - return ( ( iDrivigFlags & movePrimary ) != 0 ); }; - inline - int DrivigFlags() const { - return iDrivigFlags; }; - // returns most recently calculated distance to potential obstacle ahead - double - TrackBlock() const; - void MoveTo(TDynamicObject *to); - void DirectionInitial(); std::string TableText(std::size_t const Index) const; - int CrossRoute(TTrack *tr); /* void RouteSwitch(int d); */ - std::string OwnerName() const; - TMoverParameters const *Controlling() const { - return mvControlling; } - int Direction() const { - return iDirection; } +// members + int iLast{ 0 }; // ostatnia wypełniona pozycja w tabeli sSpeedTable; + double fLastVel = 0.0; // prędkość na poprzednio sprawdzonym torze + TTrack *tLast = nullptr; // ostatni analizowany tor + basic_event *eSignSkip = nullptr; // można pominąć ten SBL po zatrzymaniu + std::size_t SemNextIndex{ std::size_t(-1) }; + std::size_t SemNextStopIndex{ std::size_t( -1 ) }; + double dMoveLen = 0.0; // odległość przejechana od ostatniego sprawdzenia tabelki + basic_event *eSignNext = nullptr; // sygnał zmieniający prędkość, do pokazania na [F2] + +// timetable +// methods +public: + std::string TrainName() const; +private: + std::string Relation() const; + Mtable::TTrainParameters const * TrainTimetable() const; + int StationIndex() const; + int StationCount() const; + bool IsStop() const; + std::string NextStop() const; +// members + Mtable::TTrainParameters *TrainParams = nullptr; // rozkład jazdy zawsze jest, nawet jeśli pusty + std::string asNextStop; // nazwa następnego punktu zatrzymania wg rozkładu + int iStationStart = 0; // numer pierwszej stacji pokazywanej na podglądzie rozkładu + double fLastStopExpDist = -1.0; // odległość wygasania ostateniego przystanku + int iRadioChannel = 1; // numer aktualnego kanału radiowego + int iGuardRadio = 0; // numer kanału radiowego kierownika (0, gdy nie używa radia) + sound_source tsGuardSignal { sound_placement::internal }; + +// consist +// methods +public: +private: + bool CheckVehicles(TOrders user = Wait_for_orders); + bool PrepareEngine(); + bool ReleaseEngine(); + void Doors(bool const Open, int const Side = 0); + // returns true if any vehicle in the consist has an open door + bool doors_open() const; + void AutoRewident(); // ustawia hamulce w składzie +// members + double fLength = 0.0; // długość składu (do wyciągania z ograniczeń) + double fMass = 0.0; // całkowita masa do liczenia stycznej składowej grawitacji + double fAccGravity = 0.0; // przyspieszenie składowej stycznej grawitacji + int iVehicles = 0; // ilość pojazdów w składzie + int iEngineActive = 0; // ABu: Czy silnik byl juz zalaczony; Ra: postęp w załączaniu + bool IsCargoTrain{ false }; + bool IsHeavyCargoTrain{ false }; + bool IsLineBreakerClosed{ false }; // state of line breaker in all powered vehicles under control + double fReady = 0.0; // poziom odhamowania wagonów + bool Ready = false; // ABu: stan gotowosci do odjazdu - sprawdzenie odhamowania wagonow + TDynamicObject *pVehicles[ 2 ]; // skrajne pojazdy w składzie (niekoniecznie bezpośrednio sterowane) + +// logs +// methods + void PhysicsLog(); + void CloseLog(); +// members + std::ofstream LogFile; // zapis parametrow fizycznych + double LastUpdatedTime = 0.0; // czas od ostatniego logu + double ElapsedTime = 0.0; // czas od poczatku logu + +// getters +public: TDynamicObject const *Vehicle() const { return pVehicle; } TDynamicObject *Vehicle( side const Side ) const { return pVehicles[ Side ]; } +private: + std::string OwnerName() const; + +// leftovers +/* + int iRouteWanted = 3; // oczekiwany kierunek jazdy (0-stop,1-lewo,2-prawo,3-prosto) np. odpala migacz lub czeka na stan zwrotnicy +*/ + }; diff --git a/DynObj.cpp b/DynObj.cpp index 5b3e4568..fb475bc6 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -622,8 +622,12 @@ void TDynamicObject::toggle_lights() { if( true == SectionLightsActive ) { - // switch all lights off + // switch all lights off... for( auto §ion : Sections ) { + // ... but skip cab sections, their lighting ignores battery state + auto const sectionname { section.compartment->pName }; + if( sectionname.find( "cab" ) == 0 ) { continue; } + section.light_level = 0.0f; } SectionLightsActive = false; diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 63065d41..28021cee 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -1370,6 +1370,7 @@ public: bool MotorBlowersSwitch( bool State, side const Side, range_t const Notify = range_t::consist ); // traction motor fan state toggle bool MotorBlowersSwitchOff( bool State, side const Side, range_t const Notify = range_t::consist ); // traction motor fan state toggle bool MainSwitch( bool const State, range_t const Notify = range_t::consist );/*! wylacznik glowny*/ + 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*/ diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index da2a400c..cf80e601 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -490,7 +490,7 @@ int TMoverParameters::DettachStatus(int ConnectNo) // if (CouplerType==Articulated) return false; //sprzęg nie do rozpięcia - może być tylko urwany // Couplers[ConnectNo].CoupleDist=Distance(Loc,Couplers[ConnectNo].Connected->Loc,Dim,Couplers[ConnectNo].Connected->Dim); CouplerDist(ConnectNo); - if (Couplers[ConnectNo].CouplerType == TCouplerType::Screw ? Couplers[ConnectNo].CoupleDist < 0.0 : true) + if (Couplers[ConnectNo].CouplerType == TCouplerType::Screw ? Couplers[ConnectNo].CoupleDist < 0.01 : true) return -Couplers[ConnectNo].CouplingFlag; // można rozłączać, jeśli dociśnięty return (Couplers[ConnectNo].CoupleDist > 0.2) ? -Couplers[ConnectNo].CouplingFlag : Couplers[ConnectNo].CouplingFlag; @@ -563,7 +563,7 @@ bool TMoverParameters::DirectionForward() SendCtrlToNext("Direction", ActiveDir, CabNo); return true; } - else if ((ActiveDir == 1) && (MainCtrlPos == 0) && (TrainType == dt_EZT)) + else if ((ActiveDir == 1) && (MainCtrlPos == 0) && (TrainType == dt_EZT) && (EngineType != TEngineType::ElectricInductionMotor)) return MinCurrentSwitch(true); //"wysoki rozruch" EN57 return false; }; @@ -635,8 +635,9 @@ bool TMoverParameters::ChangeCab(int direction) { // if (ActiveCab+direction=0) then LastCab:=ActiveCab; ActiveCab = ActiveCab + direction; - if ((BrakeSystem == TBrakeSystem::Pneumatic) && (BrakeCtrlPosNo > 0)) - { + if( ( BrakeCtrlPosNo > 0 ) + && ( ( BrakeSystem == TBrakeSystem::Pneumatic ) + || ( BrakeSystem == TBrakeSystem::ElectroPneumatic ) ) ) { // if (BrakeHandle==FV4a) //!!!POBIERAĆ WARTOŚĆ Z KLASY ZAWORU!!! // BrakeLevelSet(-2); //BrakeCtrlPos=-2; // else if ((BrakeHandle==FVel6)||(BrakeHandle==St113)) @@ -2399,7 +2400,7 @@ bool TMoverParameters::EpFuseSwitch(bool State) bool TMoverParameters::DirectionBackward(void) { bool DB = false; - if ((ActiveDir == 1) && (MainCtrlPos == 0) && (TrainType == dt_EZT)) + if ((ActiveDir == 1) && (MainCtrlPos == 0) && (TrainType == dt_EZT) && (EngineType != TEngineType::ElectricInductionMotor)) if (MinCurrentSwitch(false)) { DB = true; // @@ -2739,66 +2740,76 @@ bool TMoverParameters::MotorBlowersSwitchOff( bool State, side const Side, range // Q: 20160713 // włączenie / wyłączenie obwodu głownego // ************************************************************************************************* -bool TMoverParameters::MainSwitch( bool const State, range_t const Notify ) -{ +bool TMoverParameters::MainSwitch( bool const State, range_t const Notify ) { + bool const initialstate { Mains || dizel_startup }; - if( ( Mains != State ) - && ( MainCtrlPosNo > 0 ) ) { + MainSwitch_( State ); - if( ( false == State ) - || ( ( ( ScndCtrlPos == 0 ) || ( EngineType == TEngineType::ElectricInductionMotor ) ) - && ( ( ConvOvldFlag == false ) || ( TrainType == dt_EZT ) ) - && ( true == NoVoltRelay ) - && ( true == OvervoltageRelay ) - && ( LastSwitchingTime > CtrlDelay ) - && ( false == TestFlag( DamageFlag, dtrain_out ) ) - && ( false == TestFlag( EngDmgFlag, 1 ) ) ) ) { - - if( true == State ) { - // switch on - if( ( EngineType == TEngineType::DieselEngine ) - || ( EngineType == TEngineType::DieselElectric ) ) { - // launch diesel engine startup procedure - dizel_startup = true; - } - else { - Mains = true; - } - } - else { - Mains = false; - // potentially knock out the pumps if their switch doesn't force them on - WaterPump.is_active &= WaterPump.is_enabled; - FuelPump.is_active &= FuelPump.is_enabled; - } - - if( ( TrainType == dt_EZT ) - && ( false == State ) ) { - - ConvOvldFlag = true; - } - - if( Mains != initialstate ) { - LastSwitchingTime = 0; - } - - if( Notify != range_t::local ) { - // pass the command to other vehicles - SendCtrlToNext( - "MainSwitch", - ( State ? 1 : 0 ), - CabNo, - ( Notify == range_t::unit ? - coupling::control | coupling::permanent : - coupling::control ) ); - } - } + if( Notify != range_t::local ) { + // pass the command to other vehicles + // TBD: pass the requested state, or the actual state? + SendCtrlToNext( + "MainSwitch", + ( State ? 1 : 0 ), + CabNo, + ( Notify == range_t::unit ? + coupling::control | coupling::permanent : + coupling::control ) ); } return ( ( Mains || dizel_startup ) != initialstate ); } +void TMoverParameters::MainSwitch_( bool const State ) { + + if( ( Mains == State ) + || ( MainCtrlPosNo == 0 ) ) { + // nothing to do + return; + } + + bool const initialstate { Mains || dizel_startup }; + + if( ( false == State ) + || ( ( ( ScndCtrlPos == 0 ) || ( EngineType == TEngineType::ElectricInductionMotor ) ) + && ( ( ConvOvldFlag == false ) || ( TrainType == dt_EZT ) ) + && ( true == NoVoltRelay ) + && ( true == OvervoltageRelay ) + && ( LastSwitchingTime > CtrlDelay ) + && ( false == TestFlag( DamageFlag, dtrain_out ) ) + && ( false == TestFlag( EngDmgFlag, 1 ) ) ) ) { + + if( true == State ) { + // switch on + if( ( EngineType == TEngineType::DieselEngine ) + || ( EngineType == TEngineType::DieselElectric ) ) { + // launch diesel engine startup procedure + dizel_startup = true; + } + else { + Mains = true; + } + } + else { + Mains = false; + // potentially knock out the pumps if their switch doesn't force them on + WaterPump.is_active &= WaterPump.is_enabled; + FuelPump.is_active &= FuelPump.is_enabled; + } + + if( ( TrainType == dt_EZT ) + && ( false == State ) ) { + + ConvOvldFlag = true; + } + + if( Mains != initialstate ) { + LastSwitchingTime = 0; + } + } +} + // ************************************************************************************************* // Q: 20160713 // włączenie / wyłączenie przetwornicy @@ -3555,9 +3566,8 @@ void TMoverParameters::UpdatePipePressure(double dt) dpMainValve = 0; - if ((BrakeCtrlPosNo > 1) /*&& (ActiveCab != 0)*/) - // with BrakePressureTable[BrakeCtrlPos] do - { + if( BrakeCtrlPosNo > 1 ) { + if ((EngineType != TEngineType::ElectricInductionMotor)) dpLocalValve = LocHandle->GetPF(std::max(LocalBrakePosA, LocalBrakePosAEIM), Hamulec->GetBCP(), ScndPipePress, dt, 0); else @@ -3573,13 +3583,21 @@ void TMoverParameters::UpdatePipePressure(double dt) temp = ScndPipePress; } Handle->SetReductor(BrakeCtrlPos2); + + if( ( ( BrakeOpModes & bom_PS ) == 0 ) + || ( ( ActiveCab != 0 ) + && ( BrakeOpModeFlag != bom_PS ) ) ) { - if ((BrakeOpModeFlag != bom_PS)) - if ((BrakeOpModeFlag < bom_EP) || ((Handle->GetPos(bh_EB) - 0.5) < BrakeCtrlPosR) || - (BrakeHandle != TBrakeHandle::MHZ_EN57)) - dpMainValve = Handle->GetPF(BrakeCtrlPosR, PipePress, temp, dt, EqvtPipePress); - else - dpMainValve = Handle->GetPF(0, PipePress, temp, dt, EqvtPipePress); + if( ( BrakeOpModeFlag < bom_EP ) + || ( ( Handle->GetPos( bh_EB ) - 0.5 ) < BrakeCtrlPosR ) + || ( ( BrakeHandle != TBrakeHandle::MHZ_EN57 ) + && ( BrakeHandle != TBrakeHandle::MHZ_K8P ) ) ) { + dpMainValve = Handle->GetPF( BrakeCtrlPosR, PipePress, temp, dt, EqvtPipePress ); + } + else { + dpMainValve = Handle->GetPF( 0, PipePress, temp, dt, EqvtPipePress ); + } + } if (dpMainValve < 0) // && (PipePressureVal > 0.01) //50 if (Compressor > ScndPipePress) @@ -4627,14 +4645,6 @@ double TMoverParameters::TractionForce( double dt ) { switch( EngineType ) { case TEngineType::ElectricSeriesMotor: { -/* - if ((Mains)) // nie wchodzić w funkcję bez potrzeby - if ( (std::max(GetTrainsetVoltage(), std::abs(Voltage)) < EnginePowerSource.CollectorParameters.MinV) || - (std::max(GetTrainsetVoltage(), std::abs(Voltage)) * EnginePowerSource.CollectorParameters.OVP > - EnginePowerSource.CollectorParameters.MaxV)) - if( MainSwitch( false, ( TrainType == dt_EZT ? range_t::unit : range_t::local ) ) ) // TODO: check whether we need to send this EMU-wide - EventFlag = true; // wywalanie szybkiego z powodu niewłaściwego napięcia -*/ // update the state of voltage relays auto const voltage { std::max( GetTrainsetVoltage(), std::abs( RunningTraction.TractionVoltage ) ) }; NoVoltRelay = ( voltage >= EnginePowerSource.CollectorParameters.MinV ); @@ -4646,6 +4656,18 @@ double TMoverParameters::TractionForce( double dt ) { break; } + case TEngineType::ElectricInductionMotor: { + // TODO: check if we can use instead the code for electricseriesmotor + if( ( Mains ) ) { + // nie wchodzić w funkcję bez potrzeby + if( ( std::max( GetTrainsetVoltage(), std::abs( RunningTraction.TractionVoltage ) ) < EnginePowerSource.CollectorParameters.MinV ) + || ( std::max( GetTrainsetVoltage(), std::abs( RunningTraction.TractionVoltage ) ) > 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 + } + } + break; + } + case TEngineType::DieselElectric: { // TODO: move this to the auto relay check when the electric engine code paths are unified StLinFlag = MotorConnectorsCheck(); @@ -5062,13 +5084,6 @@ double TMoverParameters::TractionForce( double dt ) { case TEngineType::ElectricInductionMotor: { - if( ( Mains ) ) { - // nie wchodzić w funkcję bez potrzeby - if( ( std::max( std::abs( Voltage ), GetTrainsetVoltage() ) < EnginePowerSource.CollectorParameters.MinV ) - || ( std::max( std::abs( Voltage ), GetTrainsetVoltage() ) > 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 - } - } if( true == Mains ) { //tempomat if (ScndCtrlPosNo > 1) @@ -5531,8 +5546,9 @@ bool TMoverParameters::MaxCurrentSwitch(bool State) bool TMoverParameters::MinCurrentSwitch(bool State) { bool MCS = false; - if (((EngineType == TEngineType::ElectricSeriesMotor) && (IminHi > IminLo)) || (TrainType == dt_EZT)) - { + if( ( ( EngineType == TEngineType::ElectricSeriesMotor ) && ( IminHi > IminLo ) ) + || ( ( TrainType == dt_EZT ) && ( EngineType != TEngineType::ElectricInductionMotor ) ) ) { + if (State && (Imin == IminLo)) { Imin = IminHi; @@ -9461,22 +9477,7 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C } else if (Command == "MainSwitch") { - if (CValue1 == 1) { - - if( ( EngineType == TEngineType::DieselEngine ) - || ( EngineType == TEngineType::DieselElectric ) ) { - dizel_startup = true; - } - else { - Mains = true; - } - } - else { - Mains = false; - // potentially knock out the pumps if their switch doesn't force them on - WaterPump.is_active &= WaterPump.is_enabled; - FuelPump.is_active &= FuelPump.is_enabled; - } + MainSwitch_( CValue1 > 0.0 ); OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); } else if (Command == "Direction") diff --git a/Model3d.cpp b/Model3d.cpp index 3e6fc7d9..2e95239e 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -174,10 +174,26 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic /* iVboPtr = Pos; // pozycja w VBO */ - if (!parser.expectToken("type:")) - ErrorLog("Bad model: expected submodel type definition not found while loading model \"" + Model->NameGet() + "\"" ); + auto token { parser.getToken() }; + if( token != "type:" ) { + std::string errormessage { + "Bad model: expected submodel type definition not found while loading model \"" + Model->NameGet() + "\"" + + "\ncurrent model data stream content: \"" }; + auto count { 10 }; + while( ( true == parser.getTokens() ) + && ( false == ( token = parser.peek() ).empty() ) + && ( token != "parent:" ) ) { + // skip data until next submodel, dump first few tokens in the error message + if( --count > 0 ) { + errormessage += token + " "; + } + } + errormessage += "(...)\""; + ErrorLog( errormessage ); + return 0; + } { - std::string type = parser.getToken(); + auto const type { parser.getToken() }; if (type == "mesh") eType = GL_TRIANGLES; // submodel - trójkaty else if (type == "point") @@ -190,7 +206,6 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic eType = TP_STARS; // wiele punktów świetlnych }; parser.ignoreToken(); - std::string token; parser.getTokens(1, false); // nazwa submodelu bez zmieny na małe parser >> token; Name(token); diff --git a/PyInt.cpp b/PyInt.cpp index a9924ebf..0e6ab2bc 100644 --- a/PyInt.cpp +++ b/PyInt.cpp @@ -99,13 +99,11 @@ auto python_taskqueue::init() -> bool { stringioclassname != nullptr ? PyObject_CallObject( stringioclassname, nullptr ) : nullptr ); - m_error = { ( + m_stderr = { ( stringioobject == nullptr ? nullptr : PySys_SetObject( "stderr", stringioobject ) != 0 ? nullptr : stringioobject ) }; - if( m_error == nullptr ) { goto release_and_exit; } - if( false == run_file( "abstractscreenrenderer" ) ) { goto release_and_exit; } // release the lock, save the state for future use @@ -325,13 +323,13 @@ python_taskqueue::error() { if( PyErr_Occurred() == nullptr ) { return; } - if( m_error != nullptr ) { + if( m_stderr != nullptr ) { // std err pythona jest buforowane PyErr_Print(); - auto *errortext { PyObject_CallMethod( m_error, "getvalue", nullptr ) }; + auto *errortext { PyObject_CallMethod( m_stderr, "getvalue", nullptr ) }; ErrorLog( PyString_AsString( errortext ) ); // czyscimy bufor na kolejne bledy - PyObject_CallMethod( m_error, "truncate", "i", 0 ); + PyObject_CallMethod( m_stderr, "truncate", "i", 0 ); } else { // nie dziala buffor pythona @@ -353,7 +351,7 @@ python_taskqueue::error() { } auto *tracebacktext { PyObject_Str( traceback ) }; if( tracebacktext != nullptr ) { - WriteLog( PyString_AsString( tracebacktext ) ); + ErrorLog( PyString_AsString( tracebacktext ) ); } else { WriteLog( "Python Interpreter: failed to retrieve the stack traceback" ); diff --git a/PyInt.h b/PyInt.h index 7261ade8..bc0a54c5 100644 --- a/PyInt.h +++ b/PyInt.h @@ -93,7 +93,7 @@ private: void error(); // members PyObject *m_main { nullptr }; - PyObject *m_error { nullptr }; + PyObject *m_stderr { nullptr }; PyThreadState *m_mainthread{ nullptr }; worker_array m_workers; threading::condition_variable m_condition; // wakes up the workers diff --git a/Train.cpp b/Train.cpp index 7c735041..282b403e 100644 --- a/Train.cpp +++ b/Train.cpp @@ -547,13 +547,18 @@ PyObject *TTrain::GetTrainState() { PyDict_SetItemString( dict, "velnext", PyGetFloat( driver->VelNext ) ); PyDict_SetItemString( dict, "actualproximitydist", PyGetFloat( driver->ActualProximityDist ) ); // train data + auto const *timetable{ driver->TrainTimetable() }; + PyDict_SetItemString( dict, "trainnumber", PyGetString( driver->TrainName().c_str() ) ); + PyDict_SetItemString( dict, "train_brakingmassratio", PyGetFloat( timetable->BrakeRatio ) ); + PyDict_SetItemString( dict, "train_enginetype", PyGetString( timetable->LocSeries.c_str() ) ); + PyDict_SetItemString( dict, "train_engineload", PyGetFloat( timetable->LocLoad ) ); + PyDict_SetItemString( dict, "train_stationindex", PyGetInt( driver->StationIndex() ) ); auto const stationcount { driver->StationCount() }; PyDict_SetItemString( dict, "train_stationcount", PyGetInt( stationcount ) ); if( stationcount > 0 ) { // timetable stations data, if there's any - auto const *timetable { driver->TrainTimetable() }; for( auto stationidx = 1; stationidx <= stationcount; ++stationidx ) { auto const stationlabel { "train_station" + std::to_string( stationidx ) + "_" }; auto const &timetableline { timetable->TimeTable[ stationidx ] }; @@ -567,7 +572,9 @@ PyObject *TTrain::GetTrainState() { PyDict_SetItemString( dict, ( stationlabel + "dm" ).c_str(), PyGetInt( timetableline.Dm ) ); } } + PyDict_SetItemString( dict, "train_atpassengerstop", PyGetBool( driver->IsAtPassengerStop ) ); // world state data + PyDict_SetItemString( dict, "scenario", PyGetString( Global.SceneryFile.c_str() ) ); PyDict_SetItemString( dict, "hours", PyGetInt( simulation::Time.data().wHour ) ); PyDict_SetItemString( dict, "minutes", PyGetInt( simulation::Time.data().wMinute ) ); PyDict_SetItemString( dict, "seconds", PyGetInt( simulation::Time.second() ) ); @@ -811,7 +818,7 @@ void TTrain::OnCommand_secondcontrollerincrease( TTrain *Train, command_data con if( Command.action != GLFW_RELEASE ) { // on press or hold if( ( Train->mvControlled->EngineType == TEngineType::DieselElectric ) - && ( true == Train->mvControlled->ShuntMode ) ) { + && ( true == Train->mvControlled->ShuntMode ) ) { Train->mvControlled->AnPos = clamp( Train->mvControlled->AnPos + 0.025, 0.0, 1.0 ); @@ -905,6 +912,9 @@ void TTrain::OnCommand_secondcontrollerdecreasefast( TTrain *Train, command_data } void TTrain::OnCommand_secondcontrollerset( TTrain *Train, command_data const &Command ) { + if (Command.action == GLFW_RELEASE) + return; + auto const targetposition { std::min( Command.param1, Train->mvControlled->ScndCtrlPosNo ) }; while( ( targetposition < Train->mvControlled->GetVirtualScndPos() ) && ( true == Train->mvControlled->DecScndCtrl( 1 ) ) ) { @@ -966,10 +976,13 @@ void TTrain::OnCommand_independentbrakedecreasefast( TTrain *Train, command_data void TTrain::OnCommand_independentbrakeset( TTrain *Train, command_data const &Command ) { - Train->mvControlled->LocalBrakePosA = ( - clamp( - Command.param1, - 0.0, 1.0 ) ); + if( Command.action != GLFW_RELEASE ) { + + Train->mvControlled->LocalBrakePosA = ( + clamp( + Command.param1, + 0.0, 1.0 ) ); + } /* Train->mvControlled->LocalBrakePos = ( std::round( @@ -1351,13 +1364,11 @@ void TTrain::OnCommand_trainbrakeoperationmodeincrease(TTrain *Train, command_da if( ( ( Train->mvOccupied->BrakeOpModeFlag << 1 ) & Train->mvOccupied->BrakeOpModes ) != 0 ) { // next mode Train->mvOccupied->BrakeOpModeFlag <<= 1; - // audio feedback - Train->dsbPneumaticSwitch.play(); // visual feedback Train->ggBrakeOperationModeCtrl.UpdateValue( Train->mvOccupied->BrakeOpModeFlag > 0 ? std::log2( Train->mvOccupied->BrakeOpModeFlag ) : - 0 ); + 0 ); // audio fallback } } } @@ -1369,8 +1380,6 @@ void TTrain::OnCommand_trainbrakeoperationmodedecrease(TTrain *Train, command_da if( ( ( Train->mvOccupied->BrakeOpModeFlag >> 1 ) & Train->mvOccupied->BrakeOpModes ) != 0 ) { // previous mode Train->mvOccupied->BrakeOpModeFlag >>= 1; - // audio feedback - Train->dsbPneumaticSwitch.play(); // visual feedback Train->ggBrakeOperationModeCtrl.UpdateValue( Train->mvOccupied->BrakeOpModeFlag > 0 ? @@ -5423,8 +5432,8 @@ bool TTrain::Update( double const Deltatime ) btLampkaRadioStop.Turn( mvOccupied->Radio && mvOccupied->RadioStopFlag ); btLampkaHamulecReczny.Turn(mvOccupied->ManualBrakePos > 0); // NBMX wrzesien 2003 - drzwi oraz sygnał odjazdu - btLampkaDoorLeft.Turn(mvOccupied->DoorLeftOpened); - btLampkaDoorRight.Turn(mvOccupied->DoorRightOpened); + btLampkaDoorLeft.Turn( DynamicObject->dDoorMoveL > 0.0 );// mvOccupied->DoorLeftOpened); + btLampkaDoorRight.Turn( DynamicObject->dDoorMoveR > 0.0 ); //mvOccupied ->DoorRightOpened); btLampkaBlokadaDrzwi.Turn(mvOccupied->DoorBlockedFlag()); btLampkaDoorLockOff.Turn( false == mvOccupied->DoorLockEnabled ); btLampkaDepartureSignal.Turn( mvControlled->DepartureSignal ); @@ -5733,7 +5742,12 @@ bool TTrain::Update( double const Deltatime ) InstrumentLightType == 0 ? mvControlled->Battery || mvControlled->ConverterFlag : InstrumentLightType == 1 ? mvControlled->Mains : InstrumentLightType == 2 ? mvControlled->ConverterFlag : + InstrumentLightType == 3 ? mvControlled->Battery || mvControlled->ConverterFlag : false ) }; + if( InstrumentLightType == 3 ) { + // TODO: link the light state with the state of the master key + InstrumentLightActive = true; + } btInstrumentLight.Turn( InstrumentLightActive && lightpower ); btDashboardLight.Turn( DashboardLightActive && lightpower ); btTimetableLight.Turn( TimetableLightActive && lightpower ); @@ -5791,7 +5805,9 @@ bool TTrain::Update( double const Deltatime ) ggHornLowButton.Update(); ggHornHighButton.Update(); ggWhistleButton.Update(); - ggHelperButton.UpdateValue(DynamicObject->Mechanik->HelperState); + if( DynamicObject->Mechanik != nullptr ) { + ggHelperButton.UpdateValue( DynamicObject->Mechanik->HelperState ); + } ggHelperButton.Update(); for( auto &universal : ggUniversals ) { universal.Update(); @@ -5859,7 +5875,8 @@ bool TTrain::Update( double const Deltatime ) // NOTE: crude way to have the pantographs go back up if they're dropped due to insufficient pressure etc // TODO: rework it into something more elegant, when redoing the whole consist/unit/cab etc arrangement - if( DynamicObject->Mechanik != nullptr && !DynamicObject->Mechanik->AIControllFlag ) { + if( ( DynamicObject->Mechanik == nullptr ) + || ( false == DynamicObject->Mechanik->AIControllFlag ) ) { // don't mess with the ai driving, at least not while switches don't follow ai-set vehicle state if( ( mvControlled->Battery ) || ( mvControlled->ConverterFlag ) ) { @@ -7284,8 +7301,8 @@ void TTrain::set_cab_controls( int const Cab ) { 0.f ) ); // doors // NOTE: we're relying on the cab models to have switches reversed for the rear cab(?) - ggDoorLeftButton.PutValue( mvOccupied->DoorLeftOpened ? 1.f : 0.f ); - ggDoorRightButton.PutValue( mvOccupied->DoorRightOpened ? 1.f : 0.f ); + ggDoorLeftButton.PutValue( /*mvOccupied->DoorLeftOpened*/ DynamicObject->dDoorMoveL > 0.0 ? 1.f : 0.f ); + ggDoorRightButton.PutValue( /*mvOccupied->DoorRightOpened*/ DynamicObject->dDoorMoveR > 0.0 ? 1.f : 0.f ); // door lock ggDoorSignallingButton.PutValue( mvOccupied->DoorLockEnabled ? @@ -7491,14 +7508,18 @@ bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int co btInstrumentLight.Load( Parser, DynamicObject ); InstrumentLightType = 0; } - else if( Label == "i-instrumentlight_M:" ) { + else if( Label == "i-instrumentlight_m:" ) { btInstrumentLight.Load( Parser, DynamicObject ); InstrumentLightType = 1; } - else if( Label == "i-instrumentlight_C:" ) { + else if( Label == "i-instrumentlight_c:" ) { btInstrumentLight.Load( Parser, DynamicObject ); InstrumentLightType = 2; } + else if( Label == "i-instrumentlight_a:" ) { + btInstrumentLight.Load( Parser, DynamicObject ); + InstrumentLightType = 3; + } else if (Label == "i-doors:") { int i = Parser.getToken() - 1; @@ -7734,6 +7755,12 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con gauge.Load(Parser, DynamicObject, 0.1); gauge.AssignDouble(&mvOccupied->PipePress); } + else if( Label == "scndpress:" ) { + // manometr przewodu hamulcowego + auto &gauge = Cabine[ Cabindex ].Gauge( -1 ); // pierwsza wolna gałka + gauge.Load( Parser, DynamicObject, 0.1 ); + gauge.AssignDouble( &mvOccupied->ScndPipePress ); + } else if (Label == "limpipepress:") { // manometr zbiornika sterujacego zaworu maszynisty diff --git a/Train.h b/Train.h index b2a9d03c..dcbd0ec8 100644 --- a/Train.h +++ b/Train.h @@ -509,7 +509,7 @@ public: // reszta może by?publiczna TButton btInstrumentLight; TButton btDashboardLight; TButton btTimetableLight; - int InstrumentLightType{ 0 }; // ABu 030405 - swiecenie uzaleznione od: 0-nic, 1-obw.gl, 2-przetw. + int InstrumentLightType{ 0 }; // ABu 030405 - swiecenie uzaleznione od: 0-nic, 1-obw.gl, 2-przetw., 3-rozrzad bool InstrumentLightActive{ false }; bool DashboardLightActive{ false }; bool TimetableLightActive{ false }; diff --git a/drivermouseinput.cpp b/drivermouseinput.cpp index 3ce67244..8b267ab4 100644 --- a/drivermouseinput.cpp +++ b/drivermouseinput.cpp @@ -480,6 +480,9 @@ drivermouse_input::default_bindings() { { "brakeprofiler_sw:", { user_command::brakeactingspeedsetrapid, user_command::brakeactingspeedsetpassenger } }, + { "brakeopmode_sw:", { + user_command::trainbrakeoperationmodeincrease, + user_command::trainbrakeoperationmodedecrease } }, { "maxcurrent_sw:", { user_command::motoroverloadrelaythresholdtoggle, user_command::none } }, diff --git a/driveruipanels.cpp b/driveruipanels.cpp index 86609b87..d0b45044 100644 --- a/driveruipanels.cpp +++ b/driveruipanels.cpp @@ -443,8 +443,8 @@ debug_panel::update_section_vehicle( std::vector &Output ) { std::abs( mover.enrot ) * 60, std::abs( mover.nrot ) * mover.Transmision.Ratio * 60, mover.RventRot * 60, - mover.MotorBlowers[side::front].revolutions, - mover.MotorBlowers[side::rear].revolutions, + std::abs( mover.MotorBlowers[side::front].revolutions ), + std::abs( mover.MotorBlowers[side::rear].revolutions ), mover.dizel_heat.rpmw, mover.dizel_heat.rpmw2 ); @@ -470,6 +470,7 @@ debug_panel::update_section_vehicle( std::vector &Output ) { // brakes mover.fBrakeCtrlPos, mover.LocalBrakePosA, + mover.BrakeOpModeFlag, update_vehicle_brake().c_str(), mover.LoadFlag, // cylinders @@ -482,7 +483,7 @@ debug_panel::update_section_vehicle( std::vector &Output ) { mover.ScndPipePress, mover.CntrlPipePress, // tanks - mover.Volume, + mover.Hamulec->GetBRP(), mover.Compressor, mover.Hamulec->GetCRP() ); @@ -526,37 +527,7 @@ debug_panel::update_section_vehicle( std::vector &Output ) { vehicle.GetPosition().z ); Output.emplace_back( m_buffer.data(), Global.UITextColor ); -/* - textline = " TC:" + to_string( mover.TotalCurrent, 0 ); -*/ -/* - if( mover.ManualBrakePos > 0 ) { - textline += "; manual brake on"; - } -*/ -/* - // McZapkie: warto wiedziec w jakim stanie sa przelaczniki - switch( - mover.ActiveDir * - ( mover.Imin == mover.IminLo ? - 1 : - 2 ) ) { - case 2: { textline += " >> "; break; } - case 1: { textline += " -> "; break; } - case 0: { textline += " -- "; break; } - case -1: { textline += " <- "; break; } - case -2: { textline += " << "; break; } - } - - // McZapkie: komenda i jej parametry - if( mover.CommandIn.Command != ( "" ) ) { - textline = - "C:" + mover.CommandIn.Command - + " V1=" + to_string( mover.CommandIn.Value1, 0 ) - + " V2=" + to_string( mover.CommandIn.Value2, 0 ); - } -*/ } std::string @@ -899,7 +870,10 @@ debug_panel::render_section( std::string const &Header, std::vector c if( false == ImGui::CollapsingHeader( Header.c_str() ) ) { return false; } for( auto const &line : Lines ) { - ImGui::TextColored( ImVec4( line.color.r, line.color.g, line.color.b, line.color.a ), line.data.c_str() ); + 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; } diff --git a/openglgeometrybank.cpp b/openglgeometrybank.cpp index 87214a5b..8a0bb488 100644 --- a/openglgeometrybank.cpp +++ b/openglgeometrybank.cpp @@ -190,11 +190,13 @@ opengl_vbogeometrybank::draw_( gfx::geometry_handle const &Geometry, gfx::stream m_buffercapacity = datasize; } // actual draw procedure starts here + auto &chunkrecord { m_chunkrecords[ Geometry.chunk - 1 ] }; + // sanity check; shouldn't be needed but, eh + if( chunkrecord.size == 0 ) { return; } // setup... if( m_activebuffer != m_buffer ) { bind_buffer(); } - auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ]; auto const &chunk = gfx::geometry_bank::chunk( Geometry ); if( false == chunkrecord.is_good ) { // we may potentially need to upload new buffer data before we can draw it diff --git a/renderer.cpp b/renderer.cpp index 091ca529..401ac4b6 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -580,7 +580,9 @@ opengl_renderer::Render_pass( rendermode const Mode ) { // without rain/snow we can render the cab early to limit the overdraw if( ( false == FreeFlyModeFlag ) && ( Global.Overcast <= 1.f ) ) { // precipitation happens when overcast is in 1-2 range +#ifdef EU07_DISABLECABREFLECTIONS switch_units( true, true, false ); +#endif setup_shadow_map( m_cabshadowtexture, m_cabshadowtexturematrix ); // cache shadow colour in case we need to account for cab light auto const shadowcolor { m_shadowcolor }; @@ -603,7 +605,9 @@ opengl_renderer::Render_pass( rendermode const Mode ) { Render_precipitation(); // cab render if( false == FreeFlyModeFlag ) { +#ifdef EU07_DISABLECABREFLECTIONS switch_units( true, true, false ); +#endif setup_shadow_map( m_cabshadowtexture, m_cabshadowtexturematrix ); // cache shadow colour in case we need to account for cab light auto const shadowcolor{ m_shadowcolor }; @@ -2612,7 +2616,8 @@ opengl_renderer::Render( TSubModel *Submodel ) { // material configuration: Bind_Material( null_handle ); // limit impact of dense fog on the lights - ::glFogf( GL_FOG_DENSITY, static_cast( 1.0 / std::min( Global.fFogEnd, m_fogrange * 2 ) ) ); + auto const lightrange { std::max( 500, m_fogrange * 2 ) }; // arbitrary, visibility at least 750m + ::glFogf( GL_FOG_DENSITY, static_cast( 1.0 / lightrange ) ); ::glPushAttrib( GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_POINT_BIT ); ::glDisable( GL_LIGHTING ); @@ -3147,7 +3152,8 @@ opengl_renderer::Render_Alpha( TAnimModel *Instance ) { void opengl_renderer::Render_Alpha( TTraction *Traction ) { - +/* +// NOTE: test disabled as this call is only executed as part of the colour pass double distancesquared; switch( m_renderpass.draw_mode ) { case rendermode::shadows: { @@ -3160,6 +3166,8 @@ opengl_renderer::Render_Alpha( TTraction *Traction ) { break; } } +*/ + auto const distancesquared { glm::length2( ( Traction->location() - m_renderpass.camera.position() ) / (double)Global.ZoomFactor ) / Global.fDistanceFactor }; if( ( distancesquared < Traction->m_rangesquaredmin ) || ( distancesquared >= Traction->m_rangesquaredmax ) ) { return; @@ -3208,7 +3216,8 @@ void opengl_renderer::Render_Alpha( scene::lines_node const &Lines ) { auto const &data { Lines.data() }; - +/* + // NOTE: test disabled as this call is only executed as part of the colour pass double distancesquared; switch( m_renderpass.draw_mode ) { case rendermode::shadows: { @@ -3221,6 +3230,8 @@ opengl_renderer::Render_Alpha( scene::lines_node const &Lines ) { break; } } +*/ + auto const distancesquared { glm::length2( ( data.area.center - m_renderpass.camera.position() ) / (double)Global.ZoomFactor ) / Global.fDistanceFactor }; if( ( distancesquared < data.rangesquared_min ) || ( distancesquared >= data.rangesquared_max ) ) { return; diff --git a/renderer.h b/renderer.h index 499d19a9..def51e69 100644 --- a/renderer.h +++ b/renderer.h @@ -25,6 +25,7 @@ http://mozilla.org/MPL/2.0/. //#define EU07_USE_DEBUG_CABSHADOWMAP //#define EU07_USE_DEBUG_CAMERA //#define EU07_USE_DEBUG_SOUNDEMITTERS +//#define EU07_DISABLECABREFLECTIONS struct opengl_light : public basic_light { diff --git a/scenarioloadermode.cpp b/scenarioloadermode.cpp index e412b25a..0c403aa1 100644 --- a/scenarioloadermode.cpp +++ b/scenarioloadermode.cpp @@ -36,7 +36,7 @@ scenarioloader_mode::init() { bool scenarioloader_mode::update() { - WriteLog( "\nLoading scenario..." ); + WriteLog( "\nLoading scenario \"" + Global.SceneryFile + "\"..." ); auto timestart = std::chrono::system_clock::now(); diff --git a/translation.cpp b/translation.cpp index 7881869b..469ce887 100644 --- a/translation.cpp +++ b/translation.cpp @@ -57,7 +57,7 @@ init() { "Controllers:\n master: %d(%d), secondary: %s\nEngine output: %.1f, current: %.0f\nRevolutions:\n engine: %.0f, motors: %.0f\n engine fans: %.0f, motor fans: %.0f+%.0f, cooling fans: %.0f+%.0f", " (shunt mode)", "\nTemperatures:\n engine: %.2f, oil: %.2f, water: %.2f%c%.2f", - "Brakes:\n train: %.2f, independent: %.2f, delay: %s, load flag: %d\nBrake cylinder pressures:\n train: %.2f, independent: %.2f, status: 0x%.2x\nPipe pressures:\n brake: %.2f (hat: %.2f), main: %.2f, control: %.2f\nTank pressures:\n auxiliary: %.2f, main: %.2f, control: %.2f", + "Brakes:\n train: %.2f, independent: %.2f, mode: %d, delay: %s, load flag: %d\nBrake cylinder pressures:\n train: %.2f, independent: %.2f, status: 0x%.2x\nPipe pressures:\n brake: %.2f (hat: %.2f), main: %.2f, control: %.2f\nTank pressures:\n auxiliary: %.2f, main: %.2f, control: %.2f", " pantograph: %.2f%cMT", "Forces:\n tractive: %.1f, brake: %.1f, friction: %.2f%s\nAcceleration:\n tangential: %.2f, normal: %.2f (path radius: %s)\nVelocity: %.2f, distance traveled: %.2f\nPosition: [%.2f, %.2f, %.2f]", @@ -72,6 +72,7 @@ init() { "brake acting speed", "brake acting speed: cargo", "brake acting speed: rapid", + "brake operation mode", "motor overload relay threshold", "water pump", "water pump breaker", @@ -194,7 +195,7 @@ init() { "Nastawniki:\n glowny: %d(%d), dodatkowy: %s\nMoc silnika: %.1f, prad silnika: %.0f\nObroty:\n silnik: %.0f, motory: %.0f\n went.silnika: %.0f, went.motorow: %.0f+%.0f, went.chlodnicy: %.0f+%.0f", " (tryb manewrowy)", "\nTemperatury:\n silnik: %.2f, olej: %.2f, woda: %.2f%c%.2f", - "Hamulce:\n zespolony: %.2f, pomocniczy: %.2f, nastawa: %s, ladunek: %d\nCisnienie w cylindrach:\n zespolony: %.2f, pomocniczy: %.2f, status: 0x%.2x\nCisnienia w przewodach:\n glowny: %.2f (kapturek: %.2f), zasilajacy: %.2f, kontrolny: %.2f\nCisnienia w zbiornikach:\n pomocniczy: %.2f, glowny: %.2f, sterujacy: %.2f", + "Hamulce:\n zespolony: %.2f, pomocniczy: %.2f, tryb: %d, nastawa: %s, ladunek: %d\nCisnienie w cylindrach:\n zespolony: %.2f, pomocniczy: %.2f, status: 0x%.2x\nCisnienia w przewodach:\n glowny: %.2f (kapturek: %.2f), zasilajacy: %.2f, kontrolny: %.2f\nCisnienia w zbiornikach:\n pomocniczy: %.2f, glowny: %.2f, sterujacy: %.2f", " pantograf: %.2f%cZG", "Sily:\n napedna: %.1f, hamowania: %.1f, tarcie: %.2f%s\nPrzyspieszenia:\n styczne: %.2f, normalne: %.2f (promien: %s)\nPredkosc: %.2f, pokonana odleglosc: %.2f\nPozycja: [%.2f, %.2f, %.2f]", @@ -209,6 +210,7 @@ init() { "nastawa hamulca", "nastawa hamulca: towarowy", "nastawa hamulca: pospieszny", + "tryb pracy hamulca", "zakres pradu rozruchu", "pompa wody", "wylacznik samoczynny pompy wody", @@ -318,6 +320,7 @@ init() { "brakeprofile_sw:", "brakeprofileg_sw:", "brakeprofiler_sw:", + "brakeopmode_sw:", "maxcurrent_sw:", "waterpump_sw:", "waterpumpbreaker_sw:", diff --git a/translation.h b/translation.h index 4d40b762..5bb378b1 100644 --- a/translation.h +++ b/translation.h @@ -61,6 +61,7 @@ enum string { cab_brakeprofile_sw, cab_brakeprofileg_sw, cab_brakeprofiler_sw, + cab_brakeopmode_sw, cab_maxcurrent_sw, cab_waterpump_sw, cab_waterpumpbreaker_sw, diff --git a/version.h b/version.h index 7bffdfe0..d811b3f7 100644 --- a/version.h +++ b/version.h @@ -1 +1 @@ -#define VERSION_INFO "M7 09.12.2018, based on tmj-57327f96" +#define VERSION_INFO "M7 31.12.2018, based on tmj-bdbbaaf"