diff --git a/Driver.cpp b/Driver.cpp index 7da5781b..2070329d 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -454,7 +454,7 @@ bool TController::TableAddNew() return true; // false gdy się nałoży }; -bool TController::TableNotFound(basic_event const *Event) const +bool TController::TableNotFound(basic_event const *Event, double const Distance ) const { // sprawdzenie, czy nie został już dodany do tabelki (np. podwójne W4 robi problemy) auto lookup = std::find_if( @@ -469,7 +469,11 @@ bool TController::TableNotFound(basic_event const *Event) const WriteLog( "Speed table for " + OwnerName() + " already contains event " + lookup->evEvent->m_name ); } - return lookup == sSpeedTable.end(); + // ignore duplicates which seem to be reasonably apart from each other, on account of looping tracks + // NOTE: since supplied distance is only rough approximation of distance to the event, we're using large safety margin + return ( + ( lookup == sSpeedTable.end() ) + || ( Distance - lookup->fDist > 100.0 ) ); }; void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) @@ -573,7 +577,7 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) for( auto *pEvent : events ) { if( pEvent != nullptr ) // jeśli jest semafor na tym torze { // trzeba sprawdzić tabelkę, bo dodawanie drugi raz tego samego przystanku nie jest korzystne - if (TableNotFound(pEvent)) // jeśli nie ma + if (TableNotFound(pEvent, fCurrentDistance)) // jeśli nie ma { TableAddNew(); // zawsze jest true @@ -1018,7 +1022,7 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN if (TrainParams->StationIndex < TrainParams->StationCount) { // jeśli są dalsze stacje, czekamy do godziny odjazdu - if (TrainParams->IsTimeToGo(simulation::Time.data().wHour, simulation::Time.data().wMinute)) { + if (TrainParams->IsTimeToGo(simulation::Time.data().wHour, simulation::Time.data().wMinute + simulation::Time.data().wSecond*0.0167 )) { // z dalszą akcją czekamy do godziny odjazdu IsAtPassengerStop = false; // przy jakim dystansie (stanie licznika) ma przesunąć na następny postój @@ -1470,7 +1474,8 @@ TController::braking_distance_multiplier( float const Targetvelocity ) const { if( ( mvOccupied->TrainType == dt_DMU ) && ( mvOccupied->Vel < 40.0 ) && ( Targetvelocity == 0.f ) ) { - return interpolate( 2.f, 1.f, static_cast( mvOccupied->Vel / 40.0 ) ); + auto const multiplier { clamp( 1.f + iVehicles * 0.5f, 2.f, 4.f ) }; + return interpolate( multiplier, 1.f, static_cast( mvOccupied->Vel / 40.0 ) ); } // HACK: cargo trains or trains going downhill with high braking threshold need more distance to come to a full stop if( ( fBrake_a0[ 1 ] > 0.2 ) @@ -2008,7 +2013,7 @@ void TController::AutoRewident() fBrakeReaction = 0.25; } else if( mvOccupied->TrainType == dt_DMU ) { - fNominalAccThreshold = std::max( -0.45, -fBrake_a0[ BrakeAccTableSize ] - 8 * fBrake_a1[ BrakeAccTableSize ] ); + fNominalAccThreshold = std::max( -0.75, -fBrake_a0[ BrakeAccTableSize ] - 8 * fBrake_a1[ BrakeAccTableSize ] ); fBrakeReaction = 0.25; } else if (ustaw > 16) { @@ -2120,11 +2125,12 @@ bool TController::CheckVehicles(TOrders user) while (p) { // HACK: wagony muszą mieć baterię załączoną do otwarcia drzwi... - if( ( p != pVehicle ) - && ( ( p->MoverParameters->Couplers[ end::front ].CouplingFlag & ( coupling::control ) ) == 0 ) - && ( ( p->MoverParameters->Couplers[ end::rear ].CouplingFlag & ( coupling::control ) ) == 0 ) ) { - // NOTE: don't set battery in the occupied vehicle, let the user/ai do it explicitly - p->MoverParameters->BatterySwitch( true ); + if( p != pVehicle ) { + if( ( ( p->MoverParameters->Couplers[ end::front ].CouplingFlag & ( coupling::control ) ) == 0 ) + && ( ( p->MoverParameters->Couplers[ end::rear ].CouplingFlag & ( coupling::control ) ) == 0 ) ) { + // NOTE: don't set battery in the occupied vehicle, let the user/ai do it explicitly + p->MoverParameters->BatterySwitch( true ); + } // enable heating and converter in carriages with can be heated if( p->MoverParameters->HeatingPower > 0 ) { p->MoverParameters->HeatingAllow = true; @@ -2366,6 +2372,10 @@ double TController::BrakeAccFactor() const || ( mvOccupied->Vel > VelDesired + fVelPlus ) ) ) { Factor += ( fBrakeReaction * ( /*mvOccupied->BrakeCtrlPosR*/BrakeCtrlPosition < 0.5 ? 1.5 : 1 ) ) * mvOccupied->Vel / ( std::max( 0.0, ActualProximityDist ) + 1 ) * ( ( AccDesired - AbsAccS_pub ) / fAccThreshold ); } +/* + if (mvOccupied->TrainType == dt_DMU && mvOccupied->Vel > 40 && VelNext<40) + Factor *= 1 + 0.25 * ( (1600 - VelNext * VelNext) / (mvOccupied->Vel * mvOccupied->Vel) ); +*/ return Factor; } @@ -2719,8 +2729,12 @@ bool TController::IncBrake() // NOTE: we could simplify this by doing only check of the rear coupler, but this can be quite tricky in itself // TODO: add easier ways to access front/rear coupler taking into account vehicle's direction standalone = - ( ( ( vehicle->MoverParameters->Couplers[ end::front ].Connected == nullptr ) || ( vehicle->MoverParameters->Couplers[ end::front ].CouplingFlag & coupling::control ) ) - && ( ( vehicle->MoverParameters->Couplers[ end::rear ].Connected == nullptr ) || ( vehicle->MoverParameters->Couplers[ end::rear ].CouplingFlag & coupling::control ) ) ); + ( ( ( vehicle->MoverParameters->Couplers[ end::front ].Connected == nullptr ) + || ( ( vehicle->MoverParameters->Couplers[ end::front ].CouplingFlag & coupling::control ) + && ( vehicle->MoverParameters->Couplers[ end::front ].Connected->Power > 1 ) ) ) + && ( ( vehicle->MoverParameters->Couplers[ end::rear ].Connected == nullptr ) + || ( ( vehicle->MoverParameters->Couplers[ end::rear ].CouplingFlag & coupling::control ) + && ( vehicle->MoverParameters->Couplers[ end::rear ].Connected->Power > 1 ) ) ) ); vehicle = vehicle->Next(); // kolejny pojazd, podłączony od tyłu (licząc od czoła) } } @@ -2770,17 +2784,15 @@ bool TController::IncBrake() { if( /*GBH mvOccupied->BrakeCtrlPosR*/BrakeCtrlPosition < 0.1 ) { OK = /*mvOccupied->*/BrakeLevelAdd( BrakingInitialLevel ); //GBH -/* // HACK: stronger braking to overcome SA134 engine behaviour if( ( mvOccupied->TrainType == dt_DMU ) && ( VelNext == 0.0 ) && ( fBrakeDist < 200.0 ) ) { - mvOccupied->BrakeLevelAdd( + BrakeLevelAdd( fBrakeDist / ActualProximityDist < 0.8 ? - 1.0 : - 3.0 ); + 0.5 : + 1.0 ); } -*/ } else { @@ -2801,7 +2813,9 @@ bool TController::IncBrake() } } if( /*GBH mvOccupied->BrakeCtrlPos*/BrakeCtrlPosition > 0 ) { - mvOccupied->BrakeReleaser( 0 ); + if( mvOccupied->Hamulec->Releaser() ) { + mvOccupied->BrakeReleaser( 0 ); + } } break; } @@ -2833,20 +2847,20 @@ bool TController::IncBrake() bool TController::IncBrakeEIM() { // zwiększenie hamowania bool OK = false; - switch (mvControlling->EIMCtrlType) + switch (mvOccupied->EIMCtrlType) { case 0: - OK = mvControlling->IncLocalBrakeLevel(1); + OK = mvOccupied->IncLocalBrakeLevel(1); break; case 1: - OK = mvControlling->MainCtrlPos > 0; + OK = mvOccupied->MainCtrlPos > 0; if (OK) - mvControlling->MainCtrlPos = 0; + mvOccupied->MainCtrlPos = 0; break; case 2: - OK = mvControlling->MainCtrlPos > 1; + OK = mvOccupied->MainCtrlPos > 1; if (OK) - mvControlling->MainCtrlPos = 1; + mvOccupied->MainCtrlPos = 1; break; } return OK; @@ -2855,7 +2869,8 @@ bool TController::IncBrakeEIM() bool TController::DecBrake() { // zmniejszenie siły hamowania bool OK = false; - double deltaAcc = 0; + double deltaAcc = -1.0; + double pos_diff = 1.0; switch (mvOccupied->BrakeSystem) { case TBrakeSystem::Individual: @@ -2865,7 +2880,12 @@ bool TController::DecBrake() OK = mvOccupied->DecLocalBrakeLevel(1 + floor(0.5 + fabs(AccDesired))); break; case TBrakeSystem::Pneumatic: - deltaAcc = -AccDesired*BrakeAccFactor() - (fBrake_a0[0] + 4 * (/*GBH mvOccupied->BrakeCtrlPosR*/BrakeCtrlPosition -1.0)*fBrake_a1[0]); + if( ( fBrake_a0[ 0 ] != 0.0 ) + || ( fBrake_a1[ 0 ] != 0.0 ) ) { + if( mvOccupied->TrainType == dt_DMU ) + pos_diff = 0.25; + deltaAcc = -AccDesired*BrakeAccFactor() - (fBrake_a0[0] + 4 * (/*GBH mvOccupied->BrakeCtrlPosR*/BrakeCtrlPosition - pos_diff)*fBrake_a1[0]); + } if (deltaAcc < 0) { if (/*GBH mvOccupied->BrakeCtrlPosR*/BrakeCtrlPosition > 0) @@ -2884,8 +2904,11 @@ bool TController::DecBrake() if (!OK) { OK = DecBrakeEIM(); } +/* +// NOTE: disabled, duplicate of AI's behaviour in UpdateSituation() if (mvOccupied->PipePress < 3.0) Need_BrakeRelease = true; +*/ break; case TBrakeSystem::ElectroPneumatic: if (mvOccupied->EngineType == TEngineType::ElectricInductionMotor) { @@ -2916,20 +2939,20 @@ bool TController::DecBrake() bool TController::DecBrakeEIM() { // zmniejszenie siły hamowania bool OK = false; - switch (mvControlling->EIMCtrlType) + switch (mvOccupied->EIMCtrlType) { case 0: - OK = mvControlling->DecLocalBrakeLevel(1); + OK = mvOccupied->DecLocalBrakeLevel(1); break; case 1: - OK = mvControlling->MainCtrlPos < 2; + OK = mvOccupied->MainCtrlPos < 2; if (OK) - mvControlling->MainCtrlPos = 2; + mvOccupied->MainCtrlPos = 2; break; case 2: - OK = mvControlling->MainCtrlPos < 3; + OK = mvOccupied->MainCtrlPos < 3; if (OK) - mvControlling->MainCtrlPos = 3; + mvOccupied->MainCtrlPos = 3; break; } return OK; @@ -2978,6 +3001,7 @@ bool TController::IncSpeed() // if it generates enough traction force // to build up speed to 30/40 km/h for passenger/cargo train (10 km/h less if going uphill) auto const sufficienttractionforce { std::abs( mvControlling->Ft ) > ( IsHeavyCargoTrain ? 125 : 100 ) * 1000.0 }; + auto const sufficientacceleration { AbsAccS_pub >= ( IsHeavyCargoTrain ? 0.02 : 0.04 ) }; 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 { @@ -2989,6 +3013,7 @@ bool TController::IncSpeed() ( mvControlling->Imax > mvControlling->ImaxLo ) || ( fVoltage < useseriesmodevoltage ) || ( ( true == sufficienttractionforce ) + && ( true == sufficientacceleration ) && ( mvOccupied->Vel <= ( IsCargoTrain ? 35 : 25 ) + ( seriesmodefieldshunting ? 5 : 0 ) - ( ( fAccGravity < -0.025 ) ? 10 : 0 ) ) ) ); // when not in series mode use the first available parallel mode configuration until 50/60 km/h for passenger/cargo train // (if there's only one parallel mode configuration it'll be used regardless of current speed) @@ -2998,6 +3023,7 @@ bool TController::IncSpeed() && ( useseriesmode ? mvControlling->RList[ mvControlling->MainCtrlPos ].Bn == 1 : ( ( true == sufficienttractionforce ) + && ( true == sufficientacceleration ) && ( mvOccupied->Vel <= ( IsCargoTrain ? 55 : 45 ) + ( parallelmodefieldshunting ? 5 : 0 ) ) ? mvControlling->RList[ mvControlling->MainCtrlPos ].Bn > 1 : mvControlling->MainCtrlPos == mvControlling->MainCtrlPosNo ) ) ); @@ -3437,6 +3463,21 @@ void TController::SetTimeControllers() mvOccupied->BrakeLevelSet(mvOccupied->Handle->GetPos(bh_NP)); } if (mvOccupied->BrakeHandle == TBrakeHandle::FV4a) mvOccupied->BrakeLevelSet(BrakeCtrlPosition); + if (mvOccupied->BrakeHandle == TBrakeHandle::MHZ_K8P) + { + if (BrakeCtrlPosition == 0) + mvOccupied->BrakeLevelSet(mvOccupied->Handle->GetPos(bh_RP)); + else if (BrakeCtrlPosition == -1) + mvOccupied->BrakeLevelSet(mvOccupied->Handle->GetPos(bh_FS)); + else if (BrakeCtrlPosition == -2) + mvOccupied->BrakeLevelSet(mvOccupied->Handle->GetPos(bh_NP)); + else if (BrakeCtrlPosition > 4.5) + mvOccupied->BrakeLevelSet(10); + else if (BrakeCtrlPosition > 3.70) + mvOccupied->BrakeLevelSet(9); + else + mvOccupied->BrakeLevelSet(round((BrakeCtrlPosition * 0.4 - 0.1) / 0.15)); + } } //2. Check the type of Secondary Brake Handle @@ -3468,6 +3509,26 @@ void TController::SetTimeControllers() mvOccupied->ScndCtrlPos = 1; } } + //5. Check Main Controller in Dizels + if ((mvControlling->EngineType == TEngineType::DieselEngine)&&(mvControlling->Vmax>30)) + { + int MaxPos = mvControlling->MainCtrlPosNo; + int MinPos = MaxPos; + for (int i = MaxPos; (i > 1) && (mvControlling->RList[i].Mn > 0); i--) MinPos = i; + if ((MaxPos > MinPos)&&(mvControlling->MainCtrlPos>0)&&(AccDesired>0)) + { + double Factor = 5 * (mvControlling->Vmax) / (mvControlling->Vmax + mvControlling->Vel); + int DesiredPos = MinPos + (MaxPos - MinPos)*(VelDesired > mvControlling->Vel ? (VelDesired - mvControlling->Vel) / Factor : 0); + if (DesiredPos > MaxPos) DesiredPos = MaxPos; + if (DesiredPos < MinPos) DesiredPos = MinPos; + if (!mvControlling->SlippingWheels) + { + while (mvControlling->MainCtrlPos > DesiredPos) mvControlling->DecMainCtrl(1); + if (mvControlling->Vel>mvControlling->dizel_minVelfullengage) + while (mvControlling->MainCtrlPos < DesiredPos) mvControlling->IncMainCtrl(1); + } + } + } }; void TController::CheckTimeControllers() @@ -3564,13 +3625,14 @@ void TController::Doors( bool const Open, int const Side ) { || ( false == AIControllFlag ) ) ) { // ai doesn't close the door until it's free to depart, but human driver has free reign to do stupid things if( ( pVehicle->MoverParameters->Doors.close_control == control_t::conductor ) - || ( ( true == AIControllFlag ) - && ( ( pVehicle->MoverParameters->Doors.close_control == control_t::driver ) - || ( pVehicle->MoverParameters->Doors.close_control == control_t::mixed ) ) ) ) { + || ( ( true == AIControllFlag ) ) ) { // if the door are controlled by the driver, we let the user operate them unless this user is an ai // the train conductor, if present, handles door operation also for human-driven trains - pVehicle->MoverParameters->OperateDoors( side::right, false ); - pVehicle->MoverParameters->OperateDoors( side::left, false ); + if( ( pVehicle->MoverParameters->Doors.close_control == control_t::driver ) + || ( pVehicle->MoverParameters->Doors.close_control == control_t::mixed ) ) { + pVehicle->MoverParameters->OperateDoors( side::right, false ); + pVehicle->MoverParameters->OperateDoors( side::left, false ); + } if( pVehicle->MoverParameters->Doors.permit_needed ) { pVehicle->MoverParameters->PermitDoors( side::right, false ); pVehicle->MoverParameters->PermitDoors( side::left, false ); @@ -4133,6 +4195,7 @@ TController::UpdateSituation(double dt) { // HACK: activate route scanning if an idling vehicle is activated by a human user if( ( OrderCurrentGet() == Wait_for_orders ) && ( false == AIControllFlag ) + && ( false == iEngineActive ) && ( true == mvControlling->Battery ) ) { OrderNext( Prepare_engine ); } @@ -4961,54 +5024,50 @@ TController::UpdateSituation(double dt) { // jeśli dociskanie w celu odczepienia // 3. faza odczepiania. SetVelocity(2, 0); // jazda w ustawionym kierunku z prędkością 2 - if( ( mvControlling->MainCtrlPowerPos() > 0 ) - || ( mvOccupied->BrakeSystem == TBrakeSystem::ElectroPneumatic ) ) { - // jeśli jazda - WriteLog(mvOccupied->Name + " odczepianie w kierunku " + std::to_string(mvOccupied->DirAbsolute)); - TDynamicObject *p = pVehicle; // pojazd do odczepienia, w (pVehicle) siedzi AI - int d; // numer sprzęgu, który sprawdzamy albo odczepiamy - int n = iVehicleCount; // ile wagonów ma zostać - do - { // szukanie pojazdu do odczepienia - d = p->DirectionGet() > 0 ? - end::front : - end::rear; // numer sprzęgu od strony czoła składu - // if (p->MoverParameters->Couplers[d].CouplerType==Articulated) - // //jeśli sprzęg typu wózek (za mało) - if (p->MoverParameters->Couplers[d].CouplingFlag & ctrain_depot) // jeżeli sprzęg zablokowany - // if (p->GetTrack()->) //a nie stoi na torze warsztatowym - // (ustalić po czym poznać taki tor) - ++n; // to liczymy człony jako jeden - p->MoverParameters->BrakeReleaser(1); // wyluzuj pojazd, aby dało się dopychać - // GBH p->MoverParameters->BrakeLevelSet(0); // hamulec na zero, aby nie hamował - BrakeLevelSet(gbh_RP); - if (n) - { // jeśli jeszcze nie koniec - p = p->Prev(); // kolejny w stronę czoła składu (licząc od tyłu), bo dociskamy - if (!p) - iVehicleCount = -2, - n = 0; // nie ma co dalej sprawdzać, doczepianie zakończone - } - } while (n--); - if( ( p == nullptr ) - || ( p->MoverParameters->Couplers[ d ].Connected == nullptr ) ) { - // no target, or already just virtual coupling - WriteLog( mvOccupied->Name + " didn't find anything to disconnect." ); - iVehicleCount = -2; // odczepiono, co było do odczepienia - } else if ( p->Dettach(d) == coupling::faux ) { - // tylko jeśli odepnie - WriteLog( mvOccupied->Name + " odczepiony." ); - iVehicleCount = -2; - } // a jak nie, to dociskać dalej - } - if (iVehicleCount >= 0) // zmieni się po odczepieniu - if (!mvOccupied->DecLocalBrakeLevel(1)) - { // dociśnij sklad - WriteLog( mvOccupied->Name + " dociskanie..." ); - // mvOccupied->BrakeReleaser(); //wyluzuj lokomotywę - // Ready=true; //zamiast sprawdzenia odhamowania całego składu - IncSpeed(); // dla (Ready)==false nie ruszy + if( iVehicleCount >= 0 ) { + // zmieni się po odczepieniu + WriteLog( mvOccupied->Name + " dociskanie..." ); + while( mvOccupied->DecLocalBrakeLevel( 1 ) ) { // dociśnij sklad + ; } + IncSpeed(); + } + WriteLog(mvOccupied->Name + " odczepianie w kierunku " + std::to_string(mvOccupied->DirAbsolute)); + TDynamicObject *p = pVehicle; // pojazd do odczepienia, w (pVehicle) siedzi AI + int d; // numer sprzęgu, który sprawdzamy albo odczepiamy + int n = iVehicleCount; // ile wagonów ma zostać + do + { // szukanie pojazdu do odczepienia + d = p->DirectionGet() > 0 ? + end::front : + end::rear; // numer sprzęgu od strony czoła składu + // if (p->MoverParameters->Couplers[d].CouplerType==Articulated) + // //jeśli sprzęg typu wózek (za mało) + if (p->MoverParameters->Couplers[d].CouplingFlag & ctrain_depot) // jeżeli sprzęg zablokowany + // if (p->GetTrack()->) //a nie stoi na torze warsztatowym + // (ustalić po czym poznać taki tor) + ++n; // to liczymy człony jako jeden + p->MoverParameters->BrakeReleaser(1); // wyluzuj pojazd, aby dało się dopychać + // GBH p->MoverParameters->BrakeLevelSet(0); // hamulec na zero, aby nie hamował + BrakeLevelSet(gbh_RP); + if (n) + { // jeśli jeszcze nie koniec + p = p->Prev(); // kolejny w stronę czoła składu (licząc od tyłu), bo dociskamy + if (!p) + iVehicleCount = -2, + n = 0; // nie ma co dalej sprawdzać, doczepianie zakończone + } + } while (n--); + if( ( p == nullptr ) + || ( p->MoverParameters->Couplers[ d ].Connected == nullptr ) ) { + // no target, or already just virtual coupling + WriteLog( mvOccupied->Name + " didn't find anything to disconnect." ); + iVehicleCount = -2; // odczepiono, co było do odczepienia + } else if ( p->Dettach(d) == coupling::faux ) { + // tylko jeśli odepnie + WriteLog( mvOccupied->Name + " odczepiony." ); + iVehicleCount = -2; + } // a jak nie, to dociskać dalej } if ((mvOccupied->Vel < 0.01) && !(iDrivigFlags & movePress)) { // 2. faza odczepiania: zmień kierunek na przeciwny i dociśnij @@ -5679,16 +5738,32 @@ TController::UpdateSituation(double dt) { ReactionTime = 0.25; } } - if (mvOccupied->BrakeSystem == TBrakeSystem::Pneumatic) // napełnianie uderzeniowe - if (mvOccupied->BrakeHandle == TBrakeHandle::FV4a || mvOccupied->BrakeHandle == TBrakeHandle::MHZ_6P - || mvOccupied->BrakeHandle == TBrakeHandle::M394) - { + if (mvOccupied->BrakeSystem == TBrakeSystem::Pneumatic) { + // napełnianie uderzeniowe + if( ( mvOccupied->BrakeHandle == TBrakeHandle::FV4a ) + || ( mvOccupied->BrakeHandle == TBrakeHandle::MHZ_6P ) + || ( mvOccupied->BrakeHandle == TBrakeHandle::M394 ) ) { + if( /*GBH mvOccupied->BrakeCtrlPos*/BrakeCtrlPosition == -2 ) { /*mvOccupied->*/BrakeLevelSet( gbh_RP ); } - if( ( mvOccupied->PipePress < 3.0 ) - && ( AccDesired > -0.03 ) ) { - mvOccupied->BrakeReleaser( 1 ); + + // TODO: combine all releaser handling in single decision tree instead of having bits all over the place + if( ( AccDesired > -0.03 ) + && ( false == mvOccupied->Hamulec->Releaser() ) ) { + if( mvOccupied->PipePress < 3.0 ) { + mvOccupied->BrakeReleaser( 1 ); + } + if( ( mvOccupied->BrakePress > 0.4 ) + && ( mvOccupied->Hamulec->GetCRP() > 4.9 ) ) { + // wyluzuj lokomotywę, to szybciej ruszymy + mvOccupied->BrakeReleaser( 1 ); + } + } + if( ( mvOccupied->PipePress > 3.0 ) + && ( mvOccupied->Hamulec->Releaser() ) ) { + // don't overcharge train brake pipe + mvOccupied->BrakeReleaser( 0 ); } if( ( /*GBH mvOccupied->BrakeCtrlPos*/BrakeCtrlPosition == 0 ) @@ -5696,31 +5771,37 @@ TController::UpdateSituation(double dt) { && ( AccDesired > -0.03 ) && ( VelDesired - mvOccupied->Vel > 2.0 ) ) { - if( ( mvOccupied->EqvtPipePress < 4.95 ) + if( ( mvOccupied->EqvtPipePress < 4.5 ) && ( fReady > 0.35 ) - && ( BrakeChargingCooldown >= 0.0 ) ) { + && ( BrakeChargingCooldown >= 0.0 ) + && ( ( ActualProximityDist > 100.0 ) // don't charge if we're about to be braking soon + || ( min_speed( mvOccupied->Vel, VelNext ) == mvOccupied->Vel ) ) ) { if( ( iDrivigFlags & moveOerlikons ) || ( true == IsCargoTrain ) ) { // napełnianie w Oerlikonie /* mvOccupied->BrakeLevelSet( mvOccupied->Handle->GetPos( bh_FS ) ); GBH */ - BrakeLevelSet(gbh_FS); + BrakeLevelSet( gbh_FS ); // don't charge the brakes too often, or we risk overcharging BrakeChargingCooldown = -120.0; } } +/* +// NOTE: disabled, duplicate of release activation in #5732 else if( Need_BrakeRelease ) { Need_BrakeRelease = false; mvOccupied->BrakeReleaser( 1 ); } +*/ } if( ( /*GBH mvOccupied->BrakeCtrlPos*/BrakeCtrlPosition < 0 ) && ( mvOccupied->EqvtPipePress > ( fReady < 0.25 ? 5.1 : 5.2 ) ) ) { /* GBH mvOccupied->BrakeLevelSet( mvOccupied->Handle->GetPos( bh_RP ) ); */ - BrakeLevelSet(gbh_RP); + BrakeLevelSet( gbh_RP ); } } + } #if LOGVELOCITY WriteLog("Dist=" + FloatToStrF(ActualProximityDist, ffFixed, 7, 1) + ", VelDesired=" + FloatToStrF(VelDesired, ffFixed, 7, 1) + @@ -5759,17 +5840,6 @@ TController::UpdateSituation(double dt) { AccDesired > 0.0 ) ) { // on slopes disengage the brakes only if you actually intend to accelerate while( true == DecBrake() ) { ; } // jeśli przyspieszamy, to nie hamujemy - if( ( mvOccupied->BrakePress > 0.4 ) - && ( mvOccupied->Hamulec->GetCRP() > 4.9 ) ) { - // wyluzuj lokomotywę, to szybciej ruszymy - mvOccupied->BrakeReleaser( 1 ); - } - else { - if( mvOccupied->PipePress >= 3.0 ) { - // TODO: combine all releaser handling in single decision tree instead of having bits all over the place - mvOccupied->BrakeReleaser( 0 ); - } - } } } } @@ -6524,17 +6594,18 @@ std::string TController::NextStop() const // dodać godzinę odjazdu if (!TrainParams) return ""; // tu nie powinno nigdy wejść - std::string nextstop = asNextStop; + std::string nextstop = Bezogonkow( asNextStop, true ); TMTableLine *t = TrainParams->TimeTable + TrainParams->StationIndex; if( t->Ah >= 0 ) { // przyjazd - nextstop += " przyj." + std::to_string( t->Ah ) + ":" - + ( t->Am < 10 ? "0" : "" ) + std::to_string( t->Am ); + nextstop += " przyj." + std::to_string( t->Ah ) + ":" + + to_minutes_str( t->Am, true, 3 ); + } if( t->Dh >= 0 ) { // jeśli jest godzina odjazdu nextstop += " odj." + std::to_string( t->Dh ) + ":" - + ( t->Dm < 10 ? "0" : "" ) + std::to_string( t->Dm ); + + to_minutes_str( t->Dm, true, 3 ); } return nextstop; }; @@ -6603,14 +6674,16 @@ void TController::DirectionForward(bool forward) // do przodu w obecnej kabinie while( ( mvOccupied->ActiveDir <= 0 ) && ( mvOccupied->DirectionForward() ) ) { - ; // all work is done in the header + // force scan table update + iTableDirection = 0; } } else { // do tyłu w obecnej kabinie while( ( mvOccupied->ActiveDir >= 0 ) && ( mvOccupied->DirectionBackward() ) ) { - ; // all work is done in the header + // force scan table update + iTableDirection = 0; } } if( mvOccupied->TrainType == dt_SN61 ) { diff --git a/Driver.h b/Driver.h index ec14484a..42bc47f0 100644 --- a/Driver.h +++ b/Driver.h @@ -201,6 +201,10 @@ public: inline TMoverParameters const *Controlling() const { return mvControlling; } + inline + TMoverParameters const *Occupied() const { + return mvOccupied; + } void DirectionInitial(); void DirectionChange(); inline @@ -251,6 +255,7 @@ public: bool AIControllFlag = false; // rzeczywisty/wirtualny maszynista int iOverheadZero = 0; // suma bitowa jezdy bezprądowej, bity ustawiane przez pojazdy z podniesionymi pantografami int iOverheadDown = 0; // suma bitowa opuszczenia pantografów, bity ustawiane przez pojazdy z podniesionymi pantografami + double BrakeCtrlPosition = 0.0; // intermediate position of main brake controller private: bool Psyche = false; int HelperState = 0; //stan pomocnika maszynisty @@ -280,7 +285,7 @@ private: int iCoupler = 0; // maska sprzęgu, jaką należy użyć przy łączeniu (po osiągnięciu trybu Connect), 0 gdy jazda bez łączenia 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; +// bool Need_BrakeRelease = true; 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!) @@ -318,7 +323,6 @@ private: 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 BrakeCtrlPosition = 0.0; // intermediate position of main brake controller 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 @@ -363,7 +367,7 @@ private: // Ra: metody obsługujące skanowanie toru std::vector CheckTrackEvent( TTrack *Track, double const fDirection ) const; bool TableAddNew(); - bool TableNotFound( basic_event const *Event ) const; + bool TableNotFound( basic_event const *Event, double const Distance ) const; void TableTraceRoute( double fDistance, TDynamicObject *pVehicle ); void TableCheck( double fDistance ); TCommandType TableUpdate( double &fVelDes, double &fDist, double &fNext, double &fAcc ); diff --git a/DynObj.cpp b/DynObj.cpp index bac16328..c28a729a 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -2306,7 +2306,9 @@ void TDynamicObject::AttachPrev(TDynamicObject *Object, int iType) { // Ra: doczepia Object na końcu składu (nazwa funkcji może być myląca) // Ra: używane tylko przy wczytywaniu scenerii MoverParameters->Attach( iDirection, Object->iDirection ^ 1, Object->MoverParameters, iType, true, false ); + // update neighbour data for both affected vehicles update_neighbours(); + Object->update_neighbours(); } bool TDynamicObject::UpdateForce(double dt) @@ -2768,6 +2770,8 @@ bool TDynamicObject::Update(double dt, double dt1) auto eimic = Min0R(MoverParameters->eimic, MoverParameters->eimicSpeedCtrl); MoverParameters->eimic_real = eimic; + if (MoverParameters->EIMCtrlType == 2 && MoverParameters->MainCtrlPos == 0) + eimic = -1.0; MoverParameters->SendCtrlToNext("EIMIC", Max0R(0, eimic), MoverParameters->CabNo); auto LBR = Max0R(-eimic, 0); auto eim_lb = (Mechanik->AIControllFlag || !MoverParameters->LocHandleTimeTraxx ? 0 : MoverParameters->eim_localbrake); @@ -2877,6 +2881,10 @@ bool TDynamicObject::Update(double dt, double dt1) && ( MoverParameters->BrakeOpModeFlag & bom_MED ) ) ) { FzadED = std::min( Fzad, FmaxED ); } + if (MoverParameters->EIMCtrlType == 2 && MoverParameters->MainCtrlPos < 2 && MoverParameters->eimic > -0.999) + { + FzadED = std::min(FzadED, MED_oldFED); + } if ((MoverParameters->BrakeCtrlPos == MoverParameters->Handle->GetPos(bh_EB)) && (MoverParameters->eimc[eimc_p_abed] < 0.001)) FzadED = 0; //pętla bezpieczeństwa - bez ED @@ -3054,6 +3062,8 @@ bool TDynamicObject::Update(double dt, double dt1) delete[] FzED; delete[] FzEP; delete[] FmaxEP; + + MED_oldFED = FzadED; } Mechanik->UpdateSituation(dt1); // przebłyski świadomości AI @@ -4347,10 +4357,6 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co m_materialdata.textures_alpha |= 0x08080008; } } - if( false == MoverParameters->LoadAttributes.empty() ) { - // Ra: tu wczytywanie modelu ładunku jest w porządku - mdLoad = LoadMMediaFile_mdload( MoverParameters->LoadType.name ); - } Global.asCurrentTexturePath = szTexturePath; // z powrotem defaultowa sciezka do tekstur do { token = ""; @@ -4932,6 +4938,18 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co } while( ( token != "" ) && ( token != "endmodels" ) ); + if( false == MoverParameters->LoadAttributes.empty() ) { + // Ra: tu wczytywanie modelu ładunku jest w porządku + + // bieżąca ścieżka do tekstur to dynamic/... + Global.asCurrentTexturePath = asBaseDir; + + mdLoad = LoadMMediaFile_mdload( MoverParameters->LoadType.name ); + + // z powrotem defaultowa sciezka do tekstur + Global.asCurrentTexturePath = std::string( szTexturePath ); + } + } // models else if( token == "sounds:" ) { @@ -6098,30 +6116,32 @@ TDynamicObject * TDynamicObject::ControlledFind() // problematyczna może być kwestia wybranej kabiny (w silnikowym...) // jeśli silnikowy będzie zapięty odwrotnie (tzn. -1), to i tak powinno jeździć dobrze // również hamowanie wykonuje się zaworem w członie, a nie w silnikowym... - TDynamicObject *d = this; // zaczynamy od aktualnego - if( ( d->MoverParameters->TrainType == dt_EZT ) - || ( d->MoverParameters->TrainType == dt_DMU ) ) { - // na razie dotyczy to EZT - if( ( d->NextConnected() != nullptr ) - && ( true == TestFlag( d->MoverParameters->Couplers[ end::rear ].AllowedFlag, coupling::permanent ) ) ) { - // gdy jest człon od sprzęgu 1, a sprzęg łączony warsztatowo (powiedzmy) - if( ( d->MoverParameters->Power < 1.0 ) - && ( d->NextConnected()->MoverParameters->Power > 1.0 ) ) { - // my nie mamy mocy, ale ten drugi ma - d = d->NextConnected(); // będziemy sterować tym z mocą - } - } - else if( ( d->PrevConnected() != nullptr ) - && ( true == TestFlag( d->MoverParameters->Couplers[ end::front ].AllowedFlag, coupling::permanent ) ) ) { - // gdy jest człon od sprzęgu 0, a sprzęg łączony warsztatowo (powiedzmy) - if( ( d->MoverParameters->Power < 1.0 ) - && ( d->PrevConnected()->MoverParameters->Power > 1.0 ) ) { - // my nie mamy mocy, ale ten drugi ma - d = d->PrevConnected(); // będziemy sterować tym z mocą - } + if( MoverParameters->Power > 1.0 ) { return this; } + + auto const couplingtype { ( + ( MoverParameters->TrainType == dt_EZT ) + || ( MoverParameters->TrainType == dt_DMU ) ) ? + coupling::permanent : + coupling::control + }; + // try first to look towards the rear + auto *d = this; // zaczynamy od aktualnego + + while( ( d = d->Next( couplingtype ) ) != nullptr ) { + if( d->MoverParameters->Power > 1.0 ) { + return d; } } - return d; + // if we didn't yet find a suitable vehicle try in the other direction + d = this; // zaczynamy od aktualnego + + while( ( d = d->Prev( couplingtype ) ) != nullptr ) { + if( d->MoverParameters->Power > 1.0 ) { + return d; + } + } + // if we still don't have a match give up + return this; }; //--------------------------------------------------------------------------- @@ -6602,7 +6622,8 @@ TDynamicObject::powertrain_sounds::render( TMoverParameters const &Vehicle, doub // youBy - przenioslem, bo diesel tez moze miec turbo if( Vehicle.TurboTest > 0 ) { // udawanie turbo: - auto const goalpitch { std::max( 0.025, ( engine_volume + engine_turbo.m_frequencyoffset ) * engine_turbo.m_frequencyfactor ) }; + auto const pitch_diesel { Vehicle.EngineType == TEngineType::DieselEngine ? Vehicle.enrot / Vehicle.dizel_nmax : 0 }; + auto const goalpitch { std::max( 0.025, ( engine_volume * pitch_diesel + engine_turbo.m_frequencyoffset ) * engine_turbo.m_frequencyfactor ) }; auto const goalvolume { ( ( ( Vehicle.MainCtrlPos >= Vehicle.TurboTest ) && ( Vehicle.enrot > 0.1 ) ) ? std::max( 0.0, ( engine_turbo_pitch + engine_turbo.m_amplitudeoffset ) * engine_turbo.m_amplitudefactor ) : diff --git a/DynObj.h b/DynObj.h index 93d42f80..541aca99 100644 --- a/DynObj.h +++ b/DynObj.h @@ -660,6 +660,7 @@ private: double MEDLogTime = 0; double MEDLogInactiveTime = 0; int MEDLogCount = 0; + double MED_oldFED = 0; // vehicle shaking calculations // TBD, TODO: make an object out of it diff --git a/Globals.cpp b/Globals.cpp index 9c9c4f11..a4eb5b85 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -132,7 +132,13 @@ global_settings::ConfigParse(cParser &Parser) { // selected device for audio renderer Parser.getTokens(); Parser >> AudioVolume; - AudioVolume = clamp( AudioVolume, 0.0f, 2.f ); + AudioVolume = clamp( AudioVolume, 0.f, 2.f ); + } + else if( token == "sound.volume.radio" ) { + // selected device for audio renderer + Parser.getTokens(); + Parser >> RadioVolume; + RadioVolume = clamp( RadioVolume, 0.f, 1.f ); } else if (token == "sound.maxsources") { Parser.getTokens(); diff --git a/Globals.h b/Globals.h index 3e4421c5..c69c5443 100644 --- a/Globals.h +++ b/Globals.h @@ -76,7 +76,7 @@ struct global_settings { float FrictionWeatherFactor { 1.f }; bool bLiveTraction{ true }; float Overcast{ 0.1f }; // NOTE: all this weather stuff should be moved elsewhere - glm::vec3 FogColor = { 0.6f, 0.7f, 0.8f }; + glm::vec3 FogColor = { 0.6f, 0.7f, 0.8f }; double fFogEnd{ 2000 }; std::string Season{}; // season of the year, based on simulation date std::string Weather{ "cloudy:" }; // current weather @@ -136,7 +136,8 @@ struct global_settings { double fFpsMax{ 65.0 }; // górna granica FPS, przy której promień scenerii będzie zwiększany // audio bool bSoundEnabled{ true }; - float AudioVolume{ 1.25f }; + float AudioVolume{ 1.f }; + float RadioVolume{ 0.75f }; int audio_max_sources = 30; std::string AudioRenderer; // input diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 16593aa6..0bc40d96 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -767,6 +767,7 @@ private: // ld inputs bool lock_enabled { true }; bool step_enabled { true }; + bool remote_only { false }; // door ignores local control signals // internal data int permit_preset { -1 }; // curent position of preset selection switch // vehicle parts @@ -1163,6 +1164,7 @@ public: bool CompressorAllowLocal{ true }; // local device state override (most units don't have this fitted so it's set to true not to intefere) bool CompressorGovernorLock{ false }; // indicates whether compressor pressure switch was activated due to reaching cut-out pressure start_t CompressorStart{ start_t::manual }; // whether the compressor is started manually, or another way + start_t PantographCompressorStart{ start_t::manual }; // TODO converter parameters, for when we start cleaning up mover parameters start_t ConverterStart{ start_t::manual }; // whether converter is started manually, or by other means float ConverterStartDelay{ 0.0f }; // delay (in seconds) before the converter is started, once its activation conditions are met @@ -1311,6 +1313,8 @@ public: double eim_localbrake = 0; /*nastawa hamowania dodatkowego pneumatycznego lokomotywy*/ int EIMCtrlType = 0; /*rodzaj wariantu zadajnika jazdy*/ bool SpeedCtrlTypeTime = false; /*czy tempomat sterowany czasowo*/ + int SpeedCtrlAutoTurnOffFlag = 0; /*czy tempomat sam się wyłącza*/ + bool EIMCtrlAdditionalZeros = false; /*czy ma dodatkowe zero jazdy i zero hamowania */ double eimv_pr = 0; /*realizowany procent dostepnej sily rozruchu/hamowania*/ double eimv[21]; static std::vector const eimv_labels; @@ -1345,8 +1349,6 @@ public: bool PantRearUp = false; bool PantFrontSP = true; //dzwiek patykow 'Winger 010304 bool PantRearSP = true; - int PantFrontStart = 0; //stan patykow 'Winger 160204 - int PantRearStart = 0; double PantFrontVolt = 0.0; //pantograf pod napieciem? 'Winger 160404 double PantRearVolt = 0.0; // TODO: move these switch types where they belong, cabin definition @@ -1554,6 +1556,7 @@ public: bool PermitDoors( side const Door, bool const State = true, range_t const Notify = range_t::consist ); bool ChangeDoorPermitPreset( int const Change, range_t const Notify = range_t::consist ); bool PermitDoorStep( bool const State, range_t const Notify = range_t::consist ); + bool ChangeDoorControlMode( bool const State, range_t const Notify = range_t::consist ); bool OperateDoors( side const Door, bool const State, range_t const Notify = range_t::consist ); bool LockDoors( bool const State, range_t const Notify = range_t::consist ); bool signal_departure( bool const State, range_t const Notify = range_t::consist ); // toggles departure warning diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index c2efd701..a08ae247 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -653,11 +653,18 @@ void TMoverParameters::UpdatePantVolume(double dt) { // KURS90 - sprężarka pantografów; Ra 2014-07: teraz jest to zbiornik rozrządu, chociaż to jeszcze nie tak // check the pantograph compressor while at it - if( PantCompFlag ) { - if( ( false == Battery ) - && ( false == ConverterFlag ) ) { - PantCompFlag = false; - } + if( ( PantPress < 4.2 ) + && ( ( PantographCompressorStart == start_t::automatic ) + || ( PantographCompressorStart == start_t::manualwithautofallback ) ) + && ( ( true == PantRearUp ) + || ( true == PantFrontUp ) ) ) { + // automatic start if the pressure is too low + PantCompFlag = true; + } + if( ( true == PantCompFlag ) + && ( false == Battery ) + && ( false == ConverterFlag ) ) { + PantCompFlag = false; } if (EnginePowerSource.SourceType == TPowerSource::CurrentCollector) // tylko jeśli pantografujący @@ -1684,7 +1691,9 @@ bool TMoverParameters::IncMainCtrl(int CtrlSpeed) else { ++MainCtrlPos; OK = true; - } + if ((EIMCtrlType == 0) && (SpeedCtrlAutoTurnOffFlag == 1) && (MainCtrlActualPos != MainCtrlPos)) + DecScndCtrl(2); + } break; } @@ -1846,6 +1855,8 @@ bool TMoverParameters::DecMainCtrl(int CtrlSpeed) { MainCtrlPos--; OK = true; + if ((EIMCtrlType == 0) && (SpeedCtrlAutoTurnOffFlag == 1) && (MainCtrlActualPos != MainCtrlPos)) + DecScndCtrl(2); } else if (CtrlSpeed > 1) OK = (DecMainCtrl(1) && DecMainCtrl(2)); // CtrlSpeed-1); @@ -1987,13 +1998,17 @@ bool TMoverParameters::IncScndCtrl(int CtrlSpeed) if (LastRelayTime > CtrlDelay) LastRelayTime = 0; - if ((OK) && (EngineType == TEngineType::ElectricInductionMotor) && (ScndCtrlPosNo == 1)) + if ((OK) && (EngineType == TEngineType::ElectricInductionMotor) && (ScndCtrlPosNo == 1) && (MainCtrlPos>0)) { // NOTE: round() already adds 0.5, are the ones added here as well correct? if ((Vmax < 250)) ScndCtrlActualPos = Round(Vel); else ScndCtrlActualPos = Round(Vel * 0.5); + if ((EIMCtrlType == 0)&&(SpeedCtrlAutoTurnOffFlag == 1)) + { + MainCtrlActualPos = MainCtrlPos; + } SendCtrlToNext("SpeedCntrl", ScndCtrlActualPos, CabNo); } @@ -3901,7 +3916,18 @@ void TMoverParameters::ComputeConstans(void) } Ff = TotalMassxg * (BearingF + RollF * V * V / 10.0) / 1000.0; // dorobic liczenie temperatury lozyska! - FrictConst1 = ((TotalMassxg * RollF) / 10000.0) + (Cx * Dim.W * Dim.H); + FrictConst1 = ( TotalMassxg * RollF ) / 10000.0; + // drag calculation + { + // NOTE: draft effect of previous vehicle is simplified and doesn't have much to do with reality + auto const *previousvehicle { Couplers[ ( V >= 0.0 ? end::front : end::rear ) ].Connected }; + auto dragarea { Dim.W * Dim.H }; + if( previousvehicle ) { + dragarea = std::max( 0.0, dragarea - ( 0.85 * previousvehicle->Dim.W * previousvehicle->Dim.H ) ); + } + FrictConst1 += Cx * dragarea; + } + Curvature = abs(RunningShape.R); // zero oznacza nieskończony promień if (Curvature > 0.0) Curvature = 1.0 / Curvature; @@ -4416,7 +4442,7 @@ double TMoverParameters::TractionForce( double dt ) { if( enrot != tmp ) { enrot = clamp( - enrot + ( dt / 1.25 ) * ( // TODO: equivalent of dizel_aim instead of fixed inertia + enrot + ( dt / dizel_AIM ) * ( enrot < tmp ? 1.0 : -2.0 ), // NOTE: revolutions drop faster than they rise, maybe? TBD: maybe not @@ -5915,7 +5941,6 @@ bool TMoverParameters::PantFront( bool const State, range_t const Notify ) if( PantFrontUp != State ) { PantFrontUp = State; if( State == true ) { - PantFrontStart = 0; if( Notify != range_t::local ) { // wysłanie wyłączenia do pozostałych? SendCtrlToNext( @@ -5926,7 +5951,6 @@ bool TMoverParameters::PantFront( bool const State, range_t const Notify ) } } else { - PantFrontStart = 1; if( Notify != range_t::local ) { // wysłanie wyłączenia do pozostałych? SendCtrlToNext( @@ -5968,7 +5992,6 @@ bool TMoverParameters::PantRear( bool const State, range_t const Notify ) if( PantRearUp != State ) { PantRearUp = State; if( State == true ) { - PantRearStart = 0; if( Notify != range_t::local ) { // wysłanie wyłączenia do pozostałych? SendCtrlToNext( @@ -5979,7 +6002,6 @@ bool TMoverParameters::PantRear( bool const State, range_t const Notify ) } } else { - PantRearStart = 1; if( Notify != range_t::local ) { // wysłanie wyłączenia do pozostałych? SendCtrlToNext( @@ -6014,6 +6036,13 @@ void TMoverParameters::CheckEIMIC(double dt) { case 0: eimic = (LocalBrakeRatio() > 0.01 ? -LocalBrakeRatio() : (double)MainCtrlPos / (double)MainCtrlPosNo); + if (EIMCtrlAdditionalZeros) + { + if (eimic > 0.001) + eimic = std::max(0.002, eimic * (double)MainCtrlPosNo / ((double)MainCtrlPosNo - 1.0) - 1.0 / ((double)MainCtrlPosNo - 1.0)); + if (eimic < -0.001) + eimic = std::min(-0.002, eimic * (double)LocalBrakePosNo / ((double)LocalBrakePosNo - 1.0) + 1.0 / ((double)LocalBrakePosNo - 1.0)); + } break; case 1: switch (MainCtrlPos) @@ -6064,25 +6093,34 @@ void TMoverParameters::CheckEIMIC(double dt) } break; case 2: - switch (MainCtrlPos) + if ((MainCtrlActualPos != MainCtrlPos) || (LastRelayTime>InitialCtrlDelay)) { - case 0: - eimic = -1.0; - break; - case 1: - eimic -= clamp(1.0 + eimic, 0.0, dt*0.15); //odejmuj do -1 - if (eimic > 0) eimic = 0; - break; - case 2: - eimic -= clamp(0.0 + eimic, 0.0, dt*0.15); //odejmuj do 0 - break; - case 3: - eimic += clamp(0.0 - eimic, 0.0, dt*0.15); //dodawaj do 0 - break; - case 4: - eimic += clamp(1.0 - eimic, 0.0, dt*0.15); //dodawaj do 1 - if (eimic < 0) eimic = 0; - break; + double delta = (MainCtrlActualPos == MainCtrlPos ? dt*CtrlDelay : 0.01); + switch (MainCtrlPos) + { + case 0: + case 1: + eimic -= clamp(1.0 + eimic, 0.0, delta); //odejmuj do -1 + if (eimic > 0) eimic = 0; + break; + case 2: + eimic -= clamp(0.0 + eimic, 0.0, delta); //odejmuj do 0 + break; + case 3: + eimic += clamp(0.0 - eimic, 0.0, delta); //dodawaj do 0 + break; + case 4: + eimic += clamp(1.0 - eimic, 0.0, delta); //dodawaj do 1 + if (eimic < 0) eimic = 0; + break; + } + } + if (MainCtrlActualPos == MainCtrlPos) + LastRelayTime += dt; + else + { + LastRelayTime = 0; + MainCtrlActualPos = MainCtrlPos; } break; case 3: @@ -6090,7 +6128,11 @@ void TMoverParameters::CheckEIMIC(double dt) eimic += clamp(UniCtrlList[MainCtrlPos].SetCtrlVal - eimic, 0.0, dt * UniCtrlList[MainCtrlPos].SpeedUp); //dodawaj do X eimic = clamp(eimic, UniCtrlList[MainCtrlPos].MinCtrlVal, UniCtrlList[MainCtrlPos].MaxCtrlVal); } - eimic = clamp(eimic, -1.0, 1.0); + auto const eimicpowerenabled { + ( ( true == Mains ) || ( Power == 0.0 ) ) + && ( ( Doors.instances[ side::left ].open_permit == false ) + && ( Doors.instances[ side::right ].open_permit == false ) ) }; + eimic = clamp(eimic, -1.0, eimicpowerenabled ? 1.0 : 0.0); } void TMoverParameters::CheckSpeedCtrl() @@ -6910,12 +6952,7 @@ bool TMoverParameters::PermitDoors( side const Door, bool const State, range_t c bool const initialstate { Doors.instances[Door].open_permit }; - if( ( false == Doors.permit_presets.empty() ) // HACK: for cases where preset switch is used before battery - || ( ( true == Battery ) - && ( false == Doors.is_locked ) ) ) { - - Doors.instances[ Door ].open_permit = State; - } + Doors.instances[ Door ].open_permit = State; if( Notify != range_t::local ) { @@ -6934,6 +6971,34 @@ bool TMoverParameters::PermitDoors( side const Door, bool const State, range_t c return ( Doors.instances[ Door ].open_permit != initialstate ); } +bool TMoverParameters::ChangeDoorControlMode( bool const State, range_t const Notify ) { + + auto const initialstate { Doors.remote_only }; + + Doors.remote_only = State; + if( Notify != range_t::local ) { + // wysłanie wyłączenia do pozostałych? + SendCtrlToNext( + "DoorMode", + ( State == true ? + 1 : + 0 ), + CabNo, + ( Notify == range_t::unit ? + coupling::control | coupling::permanent : + coupling::control ) ); + } + + if( true == State ) { + // when door are put in remote control mode they're automatically open + // TBD, TODO: make it dependant on config switch? + OperateDoors( side::left, true ); + OperateDoors( side::right, true ); + } + + return ( Doors.step_enabled != initialstate ); +} + bool TMoverParameters::OperateDoors( side const Door, bool const State, range_t const Notify ) { auto &door { Doors.instances[ Door ] }; @@ -7041,15 +7106,17 @@ TMoverParameters::update_doors( double const Deltatime ) { // NBMX Obsluga drzwi, MC: zuniwersalnione auto const localopencontrol { - ( Doors.open_control == control_t::passenger ) - || ( Doors.open_control == control_t::mixed ) }; + ( false == Doors.remote_only ) + && ( ( Doors.open_control == control_t::passenger ) + || ( Doors.open_control == control_t::mixed ) ) }; auto const remoteopencontrol { ( Doors.open_control == control_t::driver ) || ( Doors.open_control == control_t::conductor ) || ( Doors.open_control == control_t::mixed ) }; auto const localclosecontrol { - ( Doors.close_control == control_t::passenger ) - || ( Doors.close_control == control_t::mixed ) }; + ( false == Doors.remote_only ) + && ( ( Doors.close_control == control_t::passenger ) + || ( Doors.close_control == control_t::mixed ) ) }; auto const remoteclosecontrol { ( Doors.close_control == control_t::driver ) || ( Doors.close_control == control_t::conductor ) @@ -7077,13 +7144,18 @@ TMoverParameters::update_doors( double const Deltatime ) { && ( door.step_position <= 0.f ); door.local_open = door.local_open && ( false == door.is_open ) && ( ( false == Doors.permit_needed ) || door.open_permit ); - door.remote_open = door.remote_open && ( false == door.is_open ) && ( ( false == Doors.permit_needed ) || door.open_permit ); + door.remote_open = ( door.remote_open || Doors.remote_only ) && ( false == door.is_open ) && ( ( false == Doors.permit_needed ) || door.open_permit ); door.local_close = door.local_close && ( false == door.is_closed ); door.remote_close = door.remote_close && ( false == door.is_closed ); + auto const autoopenrequest { + ( Doors.open_control == control_t::autonomous ) + && ( ( false == Doors.permit_needed ) || door.open_permit ) + }; auto const openrequest { ( localopencontrol && door.local_open ) - || ( remoteopencontrol && door.remote_open ) }; + || ( remoteopencontrol && door.remote_open ) + || ( autoopenrequest && ( false == door.is_open ) ) }; auto const autocloserequest { ( ( Doors.auto_velocity != -1.f ) && ( Vel > Doors.auto_velocity ) ) @@ -8375,12 +8447,6 @@ void TMoverParameters::LoadFIZ_Doors( std::string const &line ) { lookup != doorcontrols.end() ? lookup->second : control_t::passenger; - - if( Doors.close_control == control_t::autonomous ) { - // convert legacy method - Doors.close_control = control_t::passenger; - Doors.auto_velocity = 10.0; - } } // automatic closing conditions extract_value( Doors.auto_duration, "DoorStayOpen", line, "" ); @@ -8683,6 +8749,7 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) { extract_value( EIMCtrlType, "EIMCtrlType", line, "" ); clamp( EIMCtrlType, 0, 3 ); LocHandleTimeTraxx = (extract_value("LocalBrakeTraxx", line) == "Yes"); + EIMCtrlAdditionalZeros = (extract_value("EIMCtrlAddZeros", line) == "Yes"); extract_value( ScndS, "ScndS", line, "" ); // brak pozycji rownoleglej przy niskiej nastawie PSR @@ -8705,6 +8772,7 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) { (extract_value("SpeedCtrlType", line) == "Time") ? true : false; + extract_value(SpeedCtrlAutoTurnOffFlag, "SpeedCtrlATOF", line, ""); // converter { @@ -8735,6 +8803,14 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) { lookup->second : start_t::manual; } + // pantograph compressor + { + auto lookup = starts.find( extract_value( "PantCompressorStart", line ) ); + PantographCompressorStart = + lookup != starts.end() ? + lookup->second : + start_t::manual; + } // fuel pump { auto lookup = starts.find( extract_value( "FuelStart", line ) ); @@ -8961,6 +9037,7 @@ void TMoverParameters::LoadFIZ_Engine( std::string const &Input ) { ImaxLo = 1; } extract_value( EngineHeatingRPM, "HeatingRPM", Input, "" ); + extract_value( dizel_AIM, "AIM", Input, "1.25" ); break; } case TEngineType::ElectricInductionMotor: { @@ -10048,6 +10125,13 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C false ); OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); } + else if( Command == "DoorMode" ) { + Doors.remote_only = ( + CValue1 == 1 ? + true : + false ); + OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); + } else if( Command == "DepartureSignal" ) { DepartureSignal = ( CValue1 == 1 ? @@ -10064,12 +10148,10 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C if ((CValue1 == 1)) { PantFrontUp = true; - PantFrontStart = 0; } else if ((CValue1 == 0)) { PantFrontUp = false; - PantFrontStart = 1; } } else @@ -10079,24 +10161,20 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C (TestFlag(Couplers[0].CouplingFlag, ctrain_controll) && (CValue2 == -1))) { PantFrontUp = true; - PantFrontStart = 0; } else { PantRearUp = true; - PantRearStart = 0; } else if ((CValue1 == 0)) if ((TestFlag(Couplers[1].CouplingFlag, ctrain_controll) && (CValue2 == 1)) || (TestFlag(Couplers[0].CouplingFlag, ctrain_controll) && (CValue2 == -1))) { PantFrontUp = false; - PantFrontStart = 1; } else { PantRearUp = false; - PantRearStart = 1; } } OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); @@ -10109,12 +10187,10 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C if ((CValue1 == 1)) { PantRearUp = true; - PantRearStart = 0; } else if ((CValue1 == 0)) { PantRearUp = false; - PantRearStart = 1; } } else @@ -10125,24 +10201,20 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C (TestFlag(Couplers[0].CouplingFlag, ctrain_controll) && (CValue2 == -1))) { PantRearUp = true; - PantRearStart = 0; } else { PantFrontUp = true; - PantFrontStart = 0; } else if ((CValue1 == 0)) if ((TestFlag(Couplers[1].CouplingFlag, ctrain_controll) && (CValue2 == 1)) || (TestFlag(Couplers[0].CouplingFlag, ctrain_controll) && (CValue2 == -1))) { PantRearUp = false; - PantRearStart = 1; } else { PantFrontUp = false; - PantFrontStart = 1; } } OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); diff --git a/McZapkie/hamulce.cpp b/McZapkie/hamulce.cpp index 5cc3dd02..59ffb5bb 100644 --- a/McZapkie/hamulce.cpp +++ b/McZapkie/hamulce.cpp @@ -385,6 +385,11 @@ void TBrake::Releaser( int const state ) BrakeStatus = (BrakeStatus & ~b_rls) | ( state * b_rls ); } +bool TBrake::Releaser() const { + + return ( ( BrakeStatus & b_rls ) == b_rls ); +} + void TBrake::SetEPS( double const nEPS ) { } @@ -1370,7 +1375,7 @@ double TLSt::GetPF( double const PP, double const dt, double const Vel ) temp = 10000; // powtarzacz — podwojny zawor zwrotny - temp = Max0R(((CVP - BCP) * BVM + ASBP * int((BrakeStatus & b_asb) == b_asb)) / temp, LBP); + temp = Max0R(((CVP - BCP) * BVM + ASBP * int((BrakeStatus & b_asb_unbrake) == b_asb_unbrake)) / temp, LBP); // luzowanie CH if ((BrakeCyl->P() > temp + 0.005) || (temp < 0.28)) // dV:=PF(0,BrakeCyl->P(),0.0015*3*sizeBC)*dt @@ -2773,6 +2778,8 @@ double TMHZ_K5P::GetPF(double i_bcp, double PP, double HP, double dt, double ep) void TMHZ_K5P::Init(double Press) { CP = Press; + Time = true; + TimeEP = true; } void TMHZ_K5P::SetReductor(double nAdj) diff --git a/McZapkie/hamulce.h b/McZapkie/hamulce.h index 6ae70eca..814718da 100644 --- a/McZapkie/hamulce.h +++ b/McZapkie/hamulce.h @@ -203,6 +203,7 @@ class TBrake { virtual double GetCRP(); //cisnienie zbiornika sterujacego bool SetBDF( int const nBDF ); //nastawiacz GPRM void Releaser( int const state ); //odluzniacz + bool Releaser() const; virtual void SetEPS( double const nEPS ); //hamulec EP virtual void SetRM( double const RMR ) {}; //ustalenie przelozenia rapida virtual void SetRV( double const RVR) {}; //ustalenie przelozenia rapida diff --git a/Train.cpp b/Train.cpp index ef4e838c..7c0c89b9 100644 --- a/Train.cpp +++ b/Train.cpp @@ -327,6 +327,7 @@ TTrain::commandhandler_map const TTrain::m_commandhandlers = { { user_command::dooropenall, &TTrain::OnCommand_dooropenall }, { user_command::doorcloseall, &TTrain::OnCommand_doorcloseall }, { user_command::doorsteptoggle, &TTrain::OnCommand_doorsteptoggle }, + { user_command::doormodetoggle, &TTrain::OnCommand_doormodetoggle }, { user_command::carcouplingincrease, &TTrain::OnCommand_carcouplingincrease }, { user_command::carcouplingdisconnect, &TTrain::OnCommand_carcouplingdisconnect }, { user_command::departureannounce, &TTrain::OnCommand_departureannounce }, @@ -393,8 +394,11 @@ TTrain::TTrain() { bHeat[ i ] = false; } for( int i = 0; i < 9; ++i ) - for( int j = 0; j < 10; ++j ) - fEIMParams[ i ][ j ] = 0.0; + for (int j = 0; j < 10; ++j) + { + fEIMParams[i][j] = 0.0; + fDieselParams[i][j] = 0.0; + } for( int i = 0; i < 20; ++i ) for( int j = 0; j < 3; ++j ) @@ -459,10 +463,10 @@ dictionary_source *TTrain::GetTrainState() { // reverser dict->insert( "direction", mover->ActiveDir ); // throttle - dict->insert( "mainctrl_pos", mover->MainCtrlPos ); - dict->insert( "main_ctrl_actual_pos", mover->MainCtrlActualPos ); - dict->insert( "scndctrl_pos", mover->ScndCtrlPos ); - dict->insert( "scnd_ctrl_actual_pos", mover->ScndCtrlActualPos ); + dict->insert( "mainctrl_pos", mvControlled->MainCtrlPos ); + dict->insert( "main_ctrl_actual_pos", mvControlled->MainCtrlActualPos ); + dict->insert( "scndctrl_pos", mvControlled->ScndCtrlPos ); + dict->insert( "scnd_ctrl_actual_pos", mvControlled->ScndCtrlActualPos ); dict->insert( "new_speed", mover->NewSpeed); // brakes dict->insert( "manual_brake", ( mvOccupied->ManualBrakePos > 0 ) ); @@ -501,6 +505,7 @@ dictionary_source *TTrain::GetTrainState() { // induction motor state data char const *TXTT[ 10 ] = { "fd", "fdt", "fdb", "pd", "pdt", "pdb", "itothv", "1", "2", "3" }; char const *TXTC[ 10 ] = { "fr", "frt", "frb", "pr", "prt", "prb", "im", "vm", "ihv", "uhv" }; + char const *TXTD[ 10 ] = { "enrot", "nrot", "fill_des", "fill_real", "clutch_des", "clutch_real", "water_temp", "oil_press", "res1", "res2" }; char const *TXTP[ 3 ] = { "bc", "bp", "sp" }; for( int j = 0; j < 10; ++j ) dict->insert( ( "eimp_t_" + std::string( TXTT[ j ] ) ), fEIMParams[ 0 ][ j ] ); @@ -508,6 +513,9 @@ dictionary_source *TTrain::GetTrainState() { for( int j = 0; j < 10; ++j ) dict->insert( ( "eimp_c" + std::to_string( i + 1 ) + "_" + std::string( TXTC[ j ] ) ), fEIMParams[ i + 1 ][ j ] ); + for (int j = 0; j < 10; ++j) + dict->insert(("diesel_param_" + std::to_string(i + 1) + "_" + std::string(TXTD[j])), fDieselParams[i + 1][j]); + dict->insert( ( "eimp_c" + std::to_string( i + 1 ) + "_ms" ), bMains[ i ] ); dict->insert( ( "eimp_c" + std::to_string( i + 1 ) + "_cv" ), fCntVol[ i ] ); dict->insert( ( "eimp_u" + std::to_string( i + 1 ) + "_pf" ), bPants[ i ][ 0 ] ); @@ -653,7 +661,8 @@ void TTrain::zero_charging_train_brake() { && ( DynamicObject->Controller != AIdriver ) && ( Global.iFeedbackMode < 3 ) && ( ( mvOccupied->BrakeHandle == TBrakeHandle::FVel6 ) - || ( mvOccupied->BrakeHandle == TBrakeHandle::MHZ_EN57 ) ) ) { + || (mvOccupied->BrakeHandle == TBrakeHandle::MHZ_EN57) + || (mvOccupied->BrakeHandle == TBrakeHandle::MHZ_K8P)) ) { // Odskakiwanie hamulce EP set_train_brake( 0 ); } @@ -785,7 +794,7 @@ void TTrain::OnCommand_mastercontrollerincrease( TTrain *Train, command_data con if( Command.action != GLFW_RELEASE ) { // on press or hold if( ( Train->ggJointCtrl.SubModel != nullptr ) - && ( Train->mvControlled->LocalBrakePosA > 0.0 ) ) { + && ( Train->mvOccupied->LocalBrakePosA > 0.0 ) ) { OnCommand_independentbrakedecrease( Train, Command ); } else { @@ -805,7 +814,7 @@ void TTrain::OnCommand_mastercontrollerincreasefast( TTrain *Train, command_data if( Command.action != GLFW_RELEASE ) { // on press or hold if( ( Train->ggJointCtrl.SubModel != nullptr ) - && ( Train->mvControlled->LocalBrakePosA > 0.0 ) ) { + && ( Train->mvOccupied->LocalBrakePosA > 0.0 ) ) { OnCommand_independentbrakedecreasefast( Train, Command ); } else { @@ -1716,10 +1725,11 @@ void TTrain::OnCommand_alerteracknowledge( TTrain *Train, command_data const &Co } } -void TTrain::OnCommand_batterytoggle( TTrain *Train, command_data const &Command ) -{ - if( Command.action == GLFW_PRESS ) { - // only reacting to press, so the switch doesn't flip back and forth if key is held down +// TODO: replace battery with a two-state device, update switch code accordingly +void TTrain::OnCommand_batterytoggle( TTrain *Train, command_data const &Command ) { + + if( Command.action != GLFW_REPEAT ) { + // keep the switch from flipping back and forth if key is held down if( false == Train->mvOccupied->Battery ) { // turn on OnCommand_batteryenable( Train, Command ); @@ -1733,16 +1743,13 @@ void TTrain::OnCommand_batterytoggle( TTrain *Train, command_data const &Command void TTrain::OnCommand_batteryenable( TTrain *Train, command_data const &Command ) { - if( true == Train->mvOccupied->Battery ) { return; } // already on - if( Command.action == GLFW_PRESS ) { - // ignore repeats - // wyłącznik jest też w SN61, ewentualnie załączać prąd na stałe z poziomu FIZ + // visual feedback + Train->ggBatteryButton.UpdateValue( 1.0, Train->dsbSwitch ); + + if( true == Train->mvOccupied->Battery ) { return; } // already on + if( Train->mvOccupied->BatterySwitch( true ) ) { - // bateria potrzebna np. do zapalenia świateł - if( Train->ggBatteryButton.SubModel ) { - Train->ggBatteryButton.UpdateValue( 1.0, Train->dsbSwitch ); - } // side-effects if( Train->mvOccupied->LightsPosNo > 0 ) { Train->SetLights(); @@ -1754,19 +1761,23 @@ void TTrain::OnCommand_batteryenable( TTrain *Train, command_data const &Command } } } + else if( Command.action == GLFW_RELEASE ) { + if( Train->ggBatteryButton.type() == TGaugeType::push ) { + // return the switch to neutral position + Train->ggBatteryButton.UpdateValue( 0.5f ); + } + } } void TTrain::OnCommand_batterydisable( TTrain *Train, command_data const &Command ) { - - if( false == Train->mvOccupied->Battery ) { return; } // already off - + // TBD, TODO: ewentualnie zablokować z FIZ, np. w samochodach się nie odłącza akumulatora if( Command.action == GLFW_PRESS ) { - // ignore repeats + // visual feedback + Train->ggBatteryButton.UpdateValue( 0.0, Train->dsbSwitch ); + + if( false == Train->mvOccupied->Battery ) { return; } // already off + if( Train->mvOccupied->BatterySwitch( false ) ) { - // ewentualnie zablokować z FIZ, np. w samochodach się nie odłącza akumulatora - if( Train->ggBatteryButton.SubModel ) { - Train->ggBatteryButton.UpdateValue( 0.0, Train->dsbSwitch ); - } // side-effects if( false == Train->mvControlled->ConverterFlag ) { // if there's no (low voltage) power source left, drop pantographs @@ -1775,6 +1786,12 @@ void TTrain::OnCommand_batterydisable( TTrain *Train, command_data const &Comman } } } + else if( Command.action == GLFW_RELEASE ) { + if( Train->ggBatteryButton.type() == TGaugeType::push ) { + // return the switch to neutral position + Train->ggBatteryButton.UpdateValue( 0.5f ); + } + } } void TTrain::OnCommand_pantographtogglefront( TTrain *Train, command_data const &cmd ) @@ -4133,7 +4150,7 @@ void TTrain::OnCommand_heatingtoggle( TTrain *Train, command_data const &Command } if( Command.action == GLFW_PRESS ) { - // only reacting to press, so the switch doesn't flip back and forth if key is held down + // ignore repeats so the switch doesn't flip back and forth if key is held down if( false == Train->mvControlled->HeatingAllow ) { // turn on OnCommand_heatingenable( Train, Command ); @@ -4143,6 +4160,14 @@ void TTrain::OnCommand_heatingtoggle( TTrain *Train, command_data const &Command OnCommand_heatingdisable( Train, Command ); } } + else if( Command.action == GLFW_RELEASE ) { + + if( Train->ggTrainHeatingButton.type() == TGaugeType::push ) { + // impulse switch + // visual feedback + Train->ggTrainHeatingButton.UpdateValue( 0.0, Train->dsbSwitch ); + } + } } void TTrain::OnCommand_heatingenable( TTrain *Train, command_data const &Command ) { @@ -4161,7 +4186,11 @@ void TTrain::OnCommand_heatingdisable( TTrain *Train, command_data const &Comman Train->mvControlled->HeatingAllow = false; // visual feedback - Train->ggTrainHeatingButton.UpdateValue( 0.0, Train->dsbSwitch ); + Train->ggTrainHeatingButton.UpdateValue( + ( Train->ggTrainHeatingButton.type() == TGaugeType::push ? + 1.0 : + 0.0 ), + Train->dsbSwitch ); } } @@ -4292,37 +4321,73 @@ void TTrain::OnCommand_doortoggleleft( TTrain *Train, command_data const &Comman void TTrain::OnCommand_doorpermitleft( TTrain *Train, command_data const &Command ) { + if( Command.action == GLFW_REPEAT ) { return; } + if( Command.action == GLFW_PRESS ) { - Train->mvOccupied->PermitDoors( - ( Train->mvOccupied->ActiveCab == 1 ? + auto const side { ( + Train->mvOccupied->ActiveCab == 1 ? side::left : - side::right ) ); + side::right ) }; - // visual feedback - Train->ggDoorLeftPermitButton.UpdateValue( 1.0, Train->dsbSwitch ); + if( Train->ggDoorLeftPermitButton.type() == TGaugeType::push ) { + // impulse switch + Train->mvOccupied->PermitDoors( side ); + // visual feedback + Train->ggDoorLeftPermitButton.UpdateValue( 1.0, Train->dsbSwitch ); + } + else { + // two-state switch + auto const newstate { !( Train->mvOccupied->Doors.instances[ side ].open_permit ) }; + + Train->mvOccupied->PermitDoors( side, newstate ); + // visual feedback + Train->ggDoorLeftPermitButton.UpdateValue( ( newstate ? 1.0 : 0.0 ), Train->dsbSwitch ); + } } else if( Command.action == GLFW_RELEASE ) { - // visual feedback - Train->ggDoorLeftPermitButton.UpdateValue( 0.0, Train->dsbSwitch ); + + if( Train->ggDoorLeftPermitButton.type() == TGaugeType::push ) { + // impulse switch + // visual feedback + Train->ggDoorLeftPermitButton.UpdateValue( 0.0, Train->dsbSwitch ); + } } } void TTrain::OnCommand_doorpermitright( TTrain *Train, command_data const &Command ) { + if( Command.action == GLFW_REPEAT ) { return; } + if( Command.action == GLFW_PRESS ) { - Train->mvOccupied->PermitDoors( - ( Train->mvOccupied->ActiveCab == 1 ? + auto const side { ( + Train->mvOccupied->ActiveCab == 1 ? side::right : - side::left ) ); + side::left ) }; - // visual feedback - Train->ggDoorRightPermitButton.UpdateValue( 1.0, Train->dsbSwitch ); + if( Train->ggDoorRightPermitButton.type() == TGaugeType::push ) { + // impulse switch + Train->mvOccupied->PermitDoors( side ); + // visual feedback + Train->ggDoorRightPermitButton.UpdateValue( 1.0, Train->dsbSwitch ); + } + else { + // two-state switch + auto const newstate { !( Train->mvOccupied->Doors.instances[ side ].open_permit ) }; + + Train->mvOccupied->PermitDoors( side, newstate ); + // visual feedback + Train->ggDoorRightPermitButton.UpdateValue( ( newstate ? 1.0 : 0.0 ), Train->dsbSwitch ); + } } else if( Command.action == GLFW_RELEASE ) { - // visual feedback - Train->ggDoorRightPermitButton.UpdateValue( 0.0, Train->dsbSwitch ); + + if( Train->ggDoorRightPermitButton.type() == TGaugeType::push ) { + // impulse switch + // visual feedback + Train->ggDoorRightPermitButton.UpdateValue( 0.0, Train->dsbSwitch ); + } } } @@ -4692,6 +4757,13 @@ void TTrain::OnCommand_doorsteptoggle( TTrain *Train, command_data const &Comman } } +void TTrain::OnCommand_doormodetoggle( TTrain *Train, command_data const &Command ) { + + if( Command.action == GLFW_PRESS ) { + Train->mvOccupied->ChangeDoorControlMode( false == Train->mvOccupied->Doors.remote_only ); + } +} + void TTrain::OnCommand_carcouplingincrease( TTrain *Train, command_data const &Command ) { if( ( true == Command.freefly ) @@ -5221,7 +5293,7 @@ bool TTrain::Update( double const Deltatime ) } bool kier = (DynamicObject->DirectionGet() * mvOccupied->ActiveCab > 0); - TDynamicObject *p = DynamicObject->GetFirstDynamic(mvOccupied->ActiveCab < 0 ? 1 : 0, 4); + TDynamicObject *p = DynamicObject->GetFirstDynamic(mvOccupied->ActiveCab < 0 ? end::rear : end::front, 4); int in = 0; fEIMParams[0][6] = 0; iCarNo = 0; @@ -5290,6 +5362,27 @@ bool TTrain::Update( double const Deltatime ) in++; iPowerNo = in; } + if ((in < 8) && (p->MoverParameters->EngineType==TEngineType::DieselEngine)) + { + fDieselParams[1 + in][0] = p->MoverParameters->enrot*60; + fDieselParams[1 + in][1] = p->MoverParameters->nrot; + fDieselParams[1 + in][2] = p->MoverParameters->RList[p->MoverParameters->MainCtrlPos].R; + fDieselParams[1 + in][3] = p->MoverParameters->dizel_fill; + fDieselParams[1 + in][4] = p->MoverParameters->RList[p->MoverParameters->MainCtrlPos].Mn; + fDieselParams[1 + in][5] = p->MoverParameters->dizel_engage; + fDieselParams[1 + in][6] = p->MoverParameters->dizel_heat.Twy; + fDieselParams[1 + in][7] = p->MoverParameters->OilPump.pressure; + //fDieselParams[1 + in][8] = p->MoverParameters-> + //fDieselParams[1 + in][9] = p->MoverParameters-> + bMains[in] = p->MoverParameters->Mains; + fCntVol[in] = p->MoverParameters->BatteryVoltage; + bFuse[in] = p->MoverParameters->FuseFlag; + bBatt[in] = p->MoverParameters->Battery; + bConv[in] = p->MoverParameters->ConverterFlag; + bHeat[in] = p->MoverParameters->Heating; + in++; + iPowerNo = in; + } // p = p->NextC(4); //prev if ((kier ? p->Next(128) : p->Prev(128)) != (kier ? p->Next(4) : p->Prev(4))) iUnitNo++; @@ -5332,16 +5425,11 @@ bool TTrain::Update( double const Deltatime ) for (int i = in; i < 8; i++) { - fEIMParams[1 + i][0] = 0; - fEIMParams[1 + i][1] = 0; - fEIMParams[1 + i][2] = 0; - fEIMParams[1 + i][3] = 0; - fEIMParams[1 + i][4] = 0; - fEIMParams[1 + i][5] = 0; - fEIMParams[1 + i][6] = 0; - fEIMParams[1 + i][7] = 0; - fEIMParams[1 + i][8] = 0; - fEIMParams[1 + i][9] = 0; + for (int j = 0; j <= 9; j++) + { + fEIMParams[1 + i][j] = 0; + fDieselParams[1 + i][j] = 0; + } } #ifdef _WIN32 if (Global.iFeedbackMode == 4) { @@ -6543,7 +6631,7 @@ void TTrain::update_sounds_radio() { auto const volume { ( true == radioenabled ) && ( message.first == iRadioChannel ) ? - 1.0 : + Global.RadioVolume : 0.0 }; message.second->gain( volume ); } @@ -7140,28 +7228,11 @@ void TTrain::DynamicSet(TDynamicObject *d) // jeździć dobrze // również hamowanie wykonuje się zaworem w członie, a nie w silnikowym... DynamicObject = d; // jedyne miejsce zmiany - mvOccupied = mvControlled = d ? DynamicObject->MoverParameters : NULL; // albo silnikowy w EZT - if (!DynamicObject) - return; - // TODO: leverage code already present in TDynamicObject::ControlledFind() - if( ( d->MoverParameters->TrainType == dt_EZT ) - || ( d->MoverParameters->TrainType == dt_DMU ) ) { + mvOccupied = mvControlled = ( d ? DynamicObject->MoverParameters : nullptr ); // albo silnikowy w EZT - if( ( d->NextConnected() != nullptr ) - && ( true == TestFlag( d->MoverParameters->Couplers[ end::rear ].AllowedFlag, coupling::permanent ) ) ) { - if( ( mvControlled->Power < 1.0 ) && ( mvControlled->Couplers[ 1 ].Connected->Power > 1.0 ) ) { - // my nie mamy mocy, ale ten drugi ma - mvControlled = DynamicObject->NextConnected()->MoverParameters; // będziemy sterować tym z mocą - } - } - else if( ( d->PrevConnected() != nullptr ) - && ( true == TestFlag( d->MoverParameters->Couplers[ end::front ].AllowedFlag, coupling::permanent ) ) ) { - if( ( mvControlled->Power < 1.0 ) && ( mvControlled->Couplers[ 0 ].Connected->Power > 1.0 ) ) { - // my nie mamy mocy, ale ten drugi ma - mvControlled = DynamicObject->PrevConnected()->MoverParameters; // będziemy sterować tym z mocą - } - } - } + if( DynamicObject == nullptr ) { return; } + + mvControlled = DynamicObject->ControlledFind()->MoverParameters; mvSecond = NULL; // gdyby się nic nie znalazło if (mvOccupied->Power > 1.0) // dwuczłonowe lub ukrotnienia, żeby nie szukać każdorazowo if (mvOccupied->Couplers[1].Connected ? @@ -7575,9 +7646,10 @@ void TTrain::clear_cab_controls() void TTrain::set_cab_controls( int const Cab ) { // switches // battery - if( true == mvOccupied->Battery ) { - ggBatteryButton.PutValue( 1.f ); - } + ggBatteryButton.PutValue( + ( ggBatteryButton.type() == TGaugeType::push ? 0.5f : + mvOccupied->Battery ? 1.f : + 0.f ) ); // motor connectors ggStLinOffButton.PutValue( ( mvControlled->StLinSwitchOff ? @@ -7740,7 +7812,12 @@ void TTrain::set_cab_controls( int const Cab ) { 1.f : 0.f ) ); // doors - // NOTE: for the time being permit switches are presumed to be impulse switches + if( ggDoorLeftPermitButton.type() != TGaugeType::push ) { + ggDoorLeftPermitButton.PutValue( mvOccupied->Doors.instances[ ( mvOccupied->ActiveCab == 1 ? side::left : side::right ) ].open_permit ? 1.f : 0.f ); + } + if( ggDoorRightPermitButton.type() != TGaugeType::push ) { + ggDoorRightPermitButton.PutValue( mvOccupied->Doors.instances[ ( mvOccupied->ActiveCab == 1 ? side::right : side::left ) ].open_permit ? 1.f : 0.f ); + } ggDoorPermitPresetButton.PutValue( mvOccupied->Doors.permit_preset ); ggDoorLeftButton.PutValue( mvOccupied->Doors.instances[ ( mvOccupied->ActiveCab == 1 ? side::left : side::right ) ].is_closed ? 0.f : 1.f ); ggDoorRightButton.PutValue( mvOccupied->Doors.instances[ ( mvOccupied->ActiveCab == 1 ? side::right : side::left ) ].is_closed ? 0.f : 1.f ); @@ -7750,8 +7827,11 @@ void TTrain::set_cab_controls( int const Cab ) { 1.f : 0.f ); // heating - if( true == mvControlled->Heating ) { - ggTrainHeatingButton.PutValue( 1.f ); + if( ggTrainHeatingButton.type() != TGaugeType::push ) { + ggTrainHeatingButton.PutValue( + mvControlled->Heating ? + 1.f : + 0.f ); } // brake acting time if( ggBrakeProfileCtrl.SubModel != nullptr ) { @@ -7969,8 +8049,8 @@ bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int co } // TODO: move viable dedicated lights to the automatic light array std::unordered_map const autolights = { - { "i-doorpermit_left:", &mvOccupied->Doors.instances[side::left].open_permit }, - { "i-doorpermit_right:", &mvOccupied->Doors.instances[ side::right ].open_permit }, + { "i-doorpermit_left:", &mvOccupied->Doors.instances[ ( mvOccupied->ActiveCab == 1 ? side::left : side::right ) ].open_permit }, + { "i-doorpermit_right:", &mvOccupied->Doors.instances[ ( mvOccupied->ActiveCab == 1 ? side::right : side::left ) ].open_permit }, { "i-doorstep:", &mvOccupied->Doors.step_enabled } }; { @@ -8140,6 +8220,7 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con } // TODO: move viable dedicated gauges to the automatic array std::unordered_map const autoboolgauges = { + { "doormode_sw:", &mvOccupied->Doors.remote_only }, { "doorstep_sw:", &mvOccupied->Doors.step_enabled }, { "coolingfans_sw:", &mvControlled->RVentForceOn } }; diff --git a/Train.h b/Train.h index bf98230e..fc472d43 100644 --- a/Train.h +++ b/Train.h @@ -331,6 +331,7 @@ class TTrain static void OnCommand_dooropenall( TTrain *Train, command_data const &Command ); static void OnCommand_doorcloseall( TTrain *Train, command_data const &Command ); static void OnCommand_doorsteptoggle( TTrain *Train, command_data const &Command ); + static void OnCommand_doormodetoggle( TTrain *Train, command_data const &Command ); static void OnCommand_carcouplingincrease( TTrain *Train, command_data const &Command ); static void OnCommand_carcouplingdisconnect( TTrain *Train, command_data const &Command ); static void OnCommand_departureannounce( TTrain *Train, command_data const &Command ); @@ -681,6 +682,7 @@ private: float fPress[20][3]; // cisnienia dla wszystkich czlonow static std::vector const fPress_labels; float fEIMParams[9][10]; // parametry dla silnikow asynchronicznych + float fDieselParams[9][10]; // parametry dla silnikow asynchronicznych int RadioChannel() const { return iRadioChannel; }; // plays provided sound from position of the radio void radio_message( sound_source *Message, int const Channel ); diff --git a/command.cpp b/command.cpp index e10f1171..94d810a9 100644 --- a/command.cpp +++ b/command.cpp @@ -155,6 +155,7 @@ commanddescription_sequence Commands_descriptions = { { "doorcloseright", command_target::vehicle, command_mode::oneoff }, { "doorcloseall", command_target::vehicle, command_mode::oneoff }, { "doorsteptoggle", command_target::vehicle, command_mode::oneoff }, + { "doormodetoggle", command_target::vehicle, command_mode::oneoff }, { "departureannounce", command_target::vehicle, command_mode::oneoff }, { "doorlocktoggle", command_target::vehicle, command_mode::oneoff }, { "pantographcompressorvalvetoggle", command_target::vehicle, command_mode::oneoff }, diff --git a/command.h b/command.h index 1d97b9b3..ad7f783e 100644 --- a/command.h +++ b/command.h @@ -148,6 +148,7 @@ enum class user_command { doorcloseright, doorcloseall, doorsteptoggle, + doormodetoggle, departureannounce, doorlocktoggle, pantographcompressorvalvetoggle, diff --git a/driverkeyboardinput.cpp b/driverkeyboardinput.cpp index 9a098b51..471e11d9 100644 --- a/driverkeyboardinput.cpp +++ b/driverkeyboardinput.cpp @@ -156,6 +156,7 @@ driverkeyboard_input::default_bindings() { // doorcloseright, { user_command::doorcloseall, GLFW_KEY_SLASH | keymodifier::control }, // doorsteptoggle, + { user_command::doormodetoggle, GLFW_KEY_SLASH | keymodifier::shift | keymodifier::control }, { user_command::departureannounce, GLFW_KEY_SLASH }, { user_command::doorlocktoggle, GLFW_KEY_S | keymodifier::control }, { user_command::pantographcompressorvalvetoggle, GLFW_KEY_V | keymodifier::control }, diff --git a/drivermouseinput.cpp b/drivermouseinput.cpp index 9bcb0d91..f20f47ca 100644 --- a/drivermouseinput.cpp +++ b/drivermouseinput.cpp @@ -22,7 +22,7 @@ http://mozilla.org/MPL/2.0/. #include "uilayer.h" #include "Logs.h" -auto const EU07_CONTROLLER_MOUSESLIDERSIZE{ 0.65 }; +auto const EU07_CONTROLLER_MOUSESLIDERSIZE{ 0.6 }; void mouse_slider::bind( user_command const &Command ) { @@ -641,6 +641,9 @@ drivermouse_input::default_bindings() { { "doorstep_sw:", { user_command::doorsteptoggle, user_command::none } }, + { "doormode_sw:", { + user_command::doormodetoggle, + user_command::none } }, { "departure_signal_bt:", { user_command::departureannounce, user_command::none } }, diff --git a/driveruipanels.cpp b/driveruipanels.cpp index ca935d87..c9e021df 100644 --- a/driveruipanels.cpp +++ b/driveruipanels.cpp @@ -226,6 +226,7 @@ timetable_panel::update() { if( false == is_open ) { return; } text_lines.clear(); + m_tablelines.clear(); auto const *train { simulation::Train }; auto const *controlled { ( train ? train->Dynamic() : nullptr ) }; @@ -236,7 +237,7 @@ timetable_panel::update() { std::snprintf( m_buffer.data(), m_buffer.size(), locale::strings[ locale::string::driver_timetable_header ].c_str(), - 40, 40, + 37, 37, locale::strings[ locale::string::driver_timetable_name ].c_str(), time.wHour, time.wMinute, @@ -271,7 +272,7 @@ timetable_panel::update() { } { // next station - auto const nextstation = Bezogonkow( owner->NextStop(), true ); + auto const nextstation = owner->NextStop(); if( false == nextstation.empty() ) { // jeśli jest podana relacja, to dodajemy punkt następnego zatrzymania auto textline = " -> " + nextstation; @@ -314,67 +315,126 @@ timetable_panel::update() { } else { + auto const loadingcolor { glm::vec4( 164.0f / 255.0f, 84.0f / 255.0f, 84.0f / 255.0f, 1.f ) }; + auto const waitcolor { glm::vec4( 164.0f / 255.0f, 132.0f / 255.0f, 84.0f / 255.0f, 1.f ) }; auto const readycolor { glm::vec4( 84.0f / 255.0f, 164.0f / 255.0f, 132.0f / 255.0f, 1.f ) }; // header - text_lines.emplace_back( "+-----+------------------------------------+-------+-----+", Global.UITextColor ); + m_tablelines.emplace_back( u8"┌─────┬────────────────────────────────────┬─────────┬─────┐", Global.UITextColor ); TMTableLine const *tableline; for( int i = owner->iStationStart; i <= table->StationCount; ++i ) { // wyświetlenie pozycji z rozkładu tableline = table->TimeTable + i; // linijka rozkładu - std::string vmax = - " " - + to_string( tableline->vmax, 0 ); - vmax = vmax.substr( vmax.size() - 3, 3 ); // z wyrównaniem do prawej - std::string const station = ( + bool vmaxchange { true }; + if( i > owner->iStationStart ) { + auto const *previoustableline { tableline - 1 }; + if( tableline->vmax == previoustableline->vmax ) { + vmaxchange = false; + } + } + std::string vmax { " " }; + if( true == vmaxchange ) { + vmax += to_string( tableline->vmax, 0 ); + vmax = vmax.substr( vmax.size() - 3, 3 ); // z wyrównaniem do prawej + } + auto const station { ( Bezogonkow( tableline->StationName, true ) + " " ) - .substr( 0, 34 ); - std::string const location = ( + .substr( 0, 34 ) }; + auto const location { ( ( tableline->km > 0.0 ? to_string( tableline->km, 2 ) : "" ) + " " ) - .substr( 0, 34 - tableline->StationWare.size() ); - std::string const arrival = ( + .substr( 0, 34 - tableline->StationWare.size() ) }; + auto const arrival { ( tableline->Ah >= 0 ? - to_string( int( 100 + tableline->Ah ) ).substr( 1, 2 ) + ":" + to_string( int( 100 + tableline->Am ) ).substr( 1, 2 ) : - " | " ); - std::string const departure = ( + to_string( int( 100 + tableline->Ah ) ).substr( 1, 2 ) + ":" + to_minutes_str( tableline->Am, true, 3 ) : + u8" │ " ) }; + auto const departure { ( tableline->Dh >= 0 ? - to_string( int( 100 + tableline->Dh ) ).substr( 1, 2 ) + ":" + to_string( int( 100 + tableline->Dm ) ).substr( 1, 2 ) : - " | " ); - auto const candeparture = ( + to_string( int( 100 + tableline->Dh ) ).substr( 1, 2 ) + ":" + to_minutes_str( tableline->Dm, true, 3 ) : + u8" │ " ) }; + auto const candeparture { ( ( owner->iStationStart < table->StationIndex ) && ( i < table->StationIndex ) && ( ( tableline->Ah < 0 ) // pass-through, always valid - || ( time.wHour * 60 + time.wMinute >= tableline->Dh * 60 + tableline->Dm ) ) ); - auto traveltime = - " " - + ( i < 2 ? "" : - tableline->Ah >= 0 ? to_string( CompareTime( table->TimeTable[ i - 1 ].Dh, table->TimeTable[ i - 1 ].Dm, tableline->Ah, tableline->Am ), 0 ) : - to_string( std::max( 0.0, CompareTime( table->TimeTable[ i - 1 ].Dh, table->TimeTable[ i - 1 ].Dm, tableline->Dh, tableline->Dm ) - 0.5 ), 0 ) ); - traveltime = traveltime.substr( traveltime.size() - 3, 3 ); // z wyrównaniem do prawej - - text_lines.emplace_back( - ( "| " + vmax + " | " + station + " | " + arrival + " | " + traveltime + " |" ), - ( candeparture ? - readycolor :// czas minął i odjazd był, to nazwa stacji będzie na zielono - Global.UITextColor ) ); - text_lines.emplace_back( - ( "| | " + location + tableline->StationWare + " | " + departure + " | |" ), - ( candeparture ? - readycolor :// czas minął i odjazd był, to nazwa stacji będzie na zielono - Global.UITextColor ) ); + || ( time.wHour * 60 + time.wMinute + time.wSecond * 0.0167 >= tableline->Dh * 60 + tableline->Dm ) ) ) }; + auto const loadchangeinprogress { ( ( static_cast( std::ceil( -1.0 * owner->fStopTime ) ) ) > 0 ) }; + auto const isatpassengerstop { ( true == owner->IsAtPassengerStop ) && ( vehicle->MoverParameters->Vel < 1.0 ) }; + auto const traveltime { ( + i < 2 ? " " : + tableline->Ah >= 0 ? to_minutes_str( CompareTime( table->TimeTable[ i - 1 ].Dh, table->TimeTable[ i - 1 ].Dm, tableline->Ah, tableline->Am ), false, 3 ) : + to_minutes_str( std::max( 0.0, CompareTime( table->TimeTable[ i - 1 ].Dh, table->TimeTable[ i - 1 ].Dm, tableline->Dh, tableline->Dm ) - 0.5 ), false, 3 ) ) }; + auto const linecolor { ( + ( i != owner->iStationStart ) ? Global.UITextColor : + loadchangeinprogress ? loadingcolor : + candeparture ? readycolor : // czas minął i odjazd był, to nazwa stacji będzie na zielono + isatpassengerstop ? waitcolor : + Global.UITextColor ) }; + m_tablelines.emplace_back( + ( u8"│ " + vmax + u8" │ " + station + u8" │ " + arrival + u8" │ " + traveltime + u8" │" ), + linecolor ); + m_tablelines.emplace_back( + ( u8"│ │ " + location + tableline->StationWare + u8" │ " + departure + u8" │ │" ), + linecolor ); // divider/footer - text_lines.emplace_back( "+-----+------------------------------------+-------+-----+", Global.UITextColor ); + if( i < table->StationCount ) { + auto const *nexttableline { tableline + 1 }; + if( tableline->vmax == nexttableline->vmax ) { + m_tablelines.emplace_back( u8"│ ├────────────────────────────────────┼─────────┼─────┤", Global.UITextColor ); + } + else { + m_tablelines.emplace_back( u8"├─────┼────────────────────────────────────┼─────────┼─────┤", Global.UITextColor ); + } + } + else { + m_tablelines.emplace_back( u8"└─────┴────────────────────────────────────┴─────────┴─────┘", Global.UITextColor ); + } } } } // is_expanded } +void +timetable_panel::render() { + + if( false == is_open ) { return; } + if( true == text_lines.empty() ) { return; } + + auto flags = + ImGuiWindowFlags_NoFocusOnAppearing + | ImGuiWindowFlags_NoCollapse + | ( size.x > 0 ? ImGuiWindowFlags_NoResize : 0 ); + + if( size.x > 0 ) { + ImGui::SetNextWindowSize( ImVec2( size.x, size.y ) ); + } + if( size_min.x > 0 ) { + ImGui::SetNextWindowSizeConstraints( ImVec2( size_min.x, size_min.y ), ImVec2( size_max.x, size_max.y ) ); + } + auto const panelname { ( + title.empty() ? + name : + title ) + + "###" + name }; + if( true == ImGui::Begin( panelname.c_str(), &is_open, flags ) ) { + for( auto const &line : text_lines ) { + ImGui::TextColored( ImVec4( line.color.r, line.color.g, line.color.b, line.color.a ), line.data.c_str() ); + } + if( is_expanded ) { + ImGui::PushStyleVar( ImGuiStyleVar_ItemSpacing, ImVec2( 1, 0 ) ); + for( auto const &line : m_tablelines ) { + ImGui::TextColored( ImVec4( line.color.r, line.color.g, line.color.b, line.color.a ), line.data.c_str() ); + } + ImGui::PopStyleVar(); + } + } + ImGui::End(); +} + void debug_panel::update() { @@ -722,7 +782,8 @@ debug_panel::update_section_engine( std::vector &Output ) { std::string parameterstext = "param value"; std::vector< std::pair > const paramvalues { - { "efill: ", mover.dizel_fill }, + { " rpm: ", mover.enrot * 60.0 }, + { "efill: ", mover.dizel_fill }, { "etorq: ", mover.dizel_Torque }, { "creal: ", mover.dizel_engage }, { "cdesi: ", mover.dizel_engagestate }, diff --git a/driveruipanels.h b/driveruipanels.h index 117da7e3..b551aa11 100644 --- a/driveruipanels.h +++ b/driveruipanels.h @@ -33,10 +33,12 @@ public: : ui_expandable_panel( Name, Isopen ) {} void update() override; + void render() override; private: // members std::array m_buffer; + std::vector m_tablelines; }; class scenario_panel : public ui_panel { @@ -48,10 +50,12 @@ public: void update() override; void render() override; + bool is_expanded{ false }; + private: // members -// std::array m_buffer; - TDynamicObject const *m_nearest { nullptr }; + std::array m_buffer; + TDynamicObject const *m_nearest { nullptr }; }; class debug_panel : public ui_panel { diff --git a/mtable.cpp b/mtable.cpp index db6a0c35..56b32a5e 100644 --- a/mtable.cpp +++ b/mtable.cpp @@ -51,7 +51,7 @@ bool TTrainParameters::IsStop() const bool TTrainParameters::UpdateMTable( scenario_time const &Time, std::string const &NewName ) { - return UpdateMTable( Time.data().wHour, Time.data().wMinute, NewName ); + return UpdateMTable( Time.data().wHour, Time.data().wMinute + Time.data().wSecond * 0.0167, NewName ); } bool TTrainParameters::UpdateMTable(double hh, double mm, std::string const &NewName) @@ -359,12 +359,12 @@ bool TTrainParameters::LoadTTfile(std::string scnpath, int iPlus, double vmax) if (s.find(hrsd) != std::string::npos) { record->Ah = atoi( s.substr(0, s.find(hrsd)).c_str()); // godzina przyjazdu - record->Am = atoi(s.substr(s.find(hrsd) + 1, s.length()).c_str()); // minuta przyjazdu + record->Am = atof(s.substr(s.find(hrsd) + 1, s.length()).c_str()); // minuta przyjazdu } else { record->Ah = TimeTable[StationCount - 1].Ah; // godzina z poprzedniej pozycji - record->Am = atoi(s.c_str()); // bo tylko minuty podane + record->Am = atof(s.c_str()); // bo tylko minuty podane } } do @@ -409,12 +409,12 @@ bool TTrainParameters::LoadTTfile(std::string scnpath, int iPlus, double vmax) if (s.find(hrsd) != std::string::npos) { record->Dh = atoi(s.substr(0, s.find(hrsd)).c_str()); // godzina odjazdu - record->Dm = atoi(s.substr(s.find(hrsd) + 1, s.length()).c_str()); // minuta odjazdu + record->Dm = atof(s.substr(s.find(hrsd) + 1, s.length()).c_str()); // minuta odjazdu } else { record->Dh = TimeTable[StationCount - 1].Dh; // godzina z poprzedniej pozycji - record->Dm = atoi(s.c_str()); // bo tylko minuty podane + record->Dm = atof(s.c_str()); // bo tylko minuty podane } } else @@ -476,20 +476,20 @@ bool TTrainParameters::LoadTTfile(std::string scnpath, int iPlus, double vmax) if( timeoffset != 0 ) // jeżeli jest przesunięcie rozkładu { long i_end = StationCount + 1; - int adjustedtime; // do zwiększania czasu + float adjustedtime; // do zwiększania czasu for (auto i = 1; i < i_end; ++i) // bez with, bo ciężko się przenosi na C++ { if ((TimeTable[i].Ah >= 0)) { - adjustedtime = clamp_circular( TimeTable[i].Ah * 60 + TimeTable[i].Am + timeoffset, 24 * 60 ); // nowe minuty - TimeTable[i].Am = adjustedtime % 60; - TimeTable[i].Ah = (adjustedtime / 60) % 24; + adjustedtime = clamp_circular( TimeTable[i].Ah * 60 + TimeTable[i].Am + timeoffset, 24 * 60 ); // nowe minuty + TimeTable[i].Am = (int(60 * adjustedtime) % 3600) / 60.f; + TimeTable[i].Ah = int((adjustedtime) / 60) % 24; } if ((TimeTable[i].Dh >= 0)) { - adjustedtime = clamp_circular( TimeTable[i].Dh * 60 + TimeTable[i].Dm + timeoffset, 24 * 60 ); // nowe minuty - TimeTable[i].Dm = adjustedtime % 60; - TimeTable[i].Dh = (adjustedtime / 60) % 24; + adjustedtime = clamp_circular( TimeTable[i].Dh * 60 + TimeTable[i].Dm + timeoffset, 24 * 60 ); // nowe minuty + TimeTable[i].Dm = (int(60 * adjustedtime) % 3600) / 60.f; + TimeTable[i].Dh = int((adjustedtime) / 60) % 24; } } } @@ -533,6 +533,8 @@ void TTrainParameters::serialize( dictionary_source *Output ) const { Output->insert( "train_enginetype", LocSeries ); Output->insert( "train_engineload", LocLoad ); + Output->insert( "train_stationfrom", Relation1 ); + Output->insert( "train_stationto", Relation2 ); Output->insert( "train_stationindex", StationIndex ); Output->insert( "train_stationcount", StationCount ); if( StationCount > 0 ) { diff --git a/mtable.h b/mtable.h index 78816aaa..b0bea8bd 100644 --- a/mtable.h +++ b/mtable.h @@ -24,18 +24,18 @@ static char const *hrsd = "."; struct TMTableLine { - double km; // kilometraz linii - double vmax; // predkosc rozkladowa przed przystankiem + float km; // kilometraz linii + float vmax; // predkosc rozkladowa przed przystankiem // StationName:string[32]; //nazwa stacji ('_' zamiast spacji) // StationWare:string[32]; //typ i wyposazenie stacji, oddz. przecinkami} std::string StationName; // nazwa stacji ('_' zamiast spacji) std::string StationWare; // typ i wyposazenie stacji, oddz. przecinkami} int TrackNo; // ilosc torow szlakowych int Ah; - int Am; // godz. i min. przyjazdu, -1 gdy bez postoju + float Am; // godz. i min. przyjazdu, -1 gdy bez postoju int Dh; - int Dm; // godz. i min. odjazdu - double tm; // czas jazdy do tej stacji w min. (z kolumny) + float Dm; // godz. i min. odjazdu + float tm; // czas jazdy do tej stacji w min. (z kolumny) TMTableLine() { km = 0; diff --git a/translation.cpp b/translation.cpp index 358da303..22b586b0 100644 --- a/translation.cpp +++ b/translation.cpp @@ -159,6 +159,7 @@ init() { "all doors (open)", "all doors (close)", "doorstep", + "door control mode", "departure signal", "upper headlight", "left headlight", @@ -352,6 +353,7 @@ init() { u8"drzwi (otwórz)", u8"drzwi (zamknij)", u8"stopień drzwi", + u8"tryb sterowania drzwiami", u8"sygnal odjazdu", u8"reflektor gorny", u8"reflektor lewy", @@ -470,6 +472,7 @@ init() { "doorallon_sw:", "dooralloff_sw:", "doorstep_sw:", + "doormode_sw", "departure_signal_bt:", "upperlight_sw:", "leftlight_sw:", diff --git a/translation.h b/translation.h index e1640ef4..94aa0062 100644 --- a/translation.h +++ b/translation.h @@ -148,6 +148,7 @@ enum string { cab_doorallon_sw, cab_dooralloff_sw, cab_doorstep_sw, + cab_doormode_sw, cab_departure_signal_bt, cab_upperlight_sw, cab_leftlight_sw, diff --git a/uilayer.cpp b/uilayer.cpp index c6702699..ce134d1f 100644 --- a/uilayer.cpp +++ b/uilayer.cpp @@ -137,6 +137,8 @@ bool ui_layer::init(GLFWwindow *Window) { 0x0020, 0x00FF, // Basic Latin + Latin Supplement 0x0100, 0x017F, // Latin Extended-A + 0x2070, 0x2079, // superscript + 0x2500, 0x256C, // box drawings 0, }; diff --git a/utilities.cpp b/utilities.cpp index e7ba7247..5ef63cd3 100644 --- a/utilities.cpp +++ b/utilities.cpp @@ -268,6 +268,24 @@ bool string_starts_with(const std::string &string, const std::string &begin) return string.compare(0, begin.length(), begin) == 0; } +std::string const fractionlabels[] = { " ", u8"¹", u8"²", u8"³", u8"⁴", u8"⁵", u8"⁶", u8"⁷", u8"⁸", u8"⁹" }; + +std::string to_minutes_str( float const Minutes, bool const Leadingzero, int const Width ) { + + float minutesintegral; + auto const minutesfractional { std::modf( Minutes, &minutesintegral ) }; + auto const width { Width - 1 }; + auto minutes = ( + std::string( width - 1, ' ' ) + + ( Leadingzero ? + to_string( 100 + minutesintegral ).substr( 1, 2 ) : + to_string( minutesintegral, 0 ) ) ); + return ( + minutes.substr( minutes.size() - width, width ) + + fractionlabels[ static_cast( std::floor( minutesfractional * 10 + 0.1 ) ) ] ); +} + + int stol_def(const std::string &str, const int &DefaultValue) { int result { DefaultValue }; diff --git a/utilities.h b/utilities.h index 5a121f42..7f6cd2ee 100644 --- a/utilities.h +++ b/utilities.h @@ -119,6 +119,7 @@ std::string to_string(double Value); std::string to_string(double Value, int precision); std::string to_string(double Value, int precision, int width); std::string to_hex_str( int const Value, int const width = 4 ); +std::string to_minutes_str( float const Minutes, bool const Leadingzero, int const Width ); inline std::string to_string(bool Value) { diff --git a/version.h b/version.h index 95181008..1fe6e4e2 100644 --- a/version.h +++ b/version.h @@ -1 +1 @@ -#define VERSION_INFO "M7 (GL3 NET) 5.04.2019, based on tmj-a081077c" +#define VERSION_INFO "M7 (gfx-work) 23.05.2019"