17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-25 09:19:47 +01:00
Files
maszyna/vehicle/Driver.h
maj00r aaccd7b9b7 Keep one primary per consist and let a secondary AI claim it.
A driver without movePrimary no longer just idles. UpdateSecondary() cuts
BrakeValveActive so a secondary AI stops fighting the primary over the brake
pipe, and - as long as no driver in the consist has iEngineActive - keeps
scanning ahead of CabOccupied, so it can take movePrimary and start the engine
once a signal permits.

scan_route() orients by CabOccupied when DirActive is 0, and
ClaimConsistPrimary() clears movePrimary on every other driver in the consist,
then calls TableClear() and CheckVehicles(), so sSpeedTable is rebuilt for the
new front and ctOwner follows it.

- walk the consist with FirstFind()/Neighbour() instead of Next()/Prev(), which
  stopped at the starting vehicle and mishandled reversed members;
- ReassignConsistPrimary() picks a single driver after any coupling change,
  both from TDynamicObject::couple() and from the AI path;
- IsOccupiedByAnotherConsist() decides membership with BelongsToConsist() and
  takes a signed distance, so a consist left behind no longer cuts the scan
  short. DistanceToAnotherConsist() hands back the gap it already computes, so
  OtherConsistAtSignal() counts a foreign vehicle only while it stands closer
  than the signal itself, instead of comparing track starts;
- TSpeedPos::Set() marks an entry with spNotOurShuntSignal when a
  spShuntSemaphor was already permitting as the entry was added. A shunting
  signal does not address its permission to a particular consist, so a dormant
  driver does not wake up on a permission it did not see light up; the flag
  drops once the signal shows stop, so its own turn is never blocked. spSemaphor
  is excluded: a semaphore sets a route for us and permits us even when found
  already lit. The check reads SemPermitIndex - the entry that produced the
  permitting command - because SemNextIndex can point at an earlier signal.
  The flag is also stamped whenever the permission turns out to belong to a
  consist standing in front of us - both where TableUpdateEvent() already knows
  the signal is for the vehicle ahead, and in check_route_ahead() - so the
  addressee pulling away does not hand the still-lit signal over to whoever
  waited behind it;
- the scantable debug panel lists the flags by name (spNotOurShuntSignal shows
  as not-ours) and folds the nearest vehicle ahead into the table by its own
  distance.
2026-09-06 13:21:44 +02:00

707 lines
35 KiB
C++

/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#pragma once
#include <string>
#include "utilities/Classes.h"
#include "utilities/utilities.h"
#include "MOVER.h"
#include "audio/sound.h"
#include "vehicle/DynObj.h"
#include "world/mtable.h"
#include "utilities/translation.h"
#include "application/driverhints.h"
auto const EU07_AI_ACCELERATION = 0.05;
auto const EU07_AI_NOACCELERATION = -0.05;
auto const EU07_AI_BRAKINGTESTACCELERATION = -0.06;
auto const EU07_AI_NOMOVEMENT = 0.05; // standstill velocity threshold
auto const EU07_AI_MOVEMENT = 1.0; // deliberate movement velocity threshold
auto const EU07_AI_SECONDARYSCANINTERVAL = 5.0; // s - a secondary scans for a wake-up less often than the primary
auto const EU07_AI_SPEEDLIMITEXTENDSBEYONDSCANRANGE = 10000.0;
enum TOrders
{ // rozkazy dla AI
Wait_for_orders = 0, // czekanie na dostarczenie następnych rozkazów
// operacje tymczasowe
Prepare_engine = 1 << 0, // włączenie silnika
Release_engine = 1 << 1, // wyłączenie silnika
Change_direction = 1 << 2, // zmiana kierunku (bez skanowania sygnalizacji)
Connect = 1 << 3, // podłączanie wagonów (z częściowym skanowaniem sygnalizacji)
Disconnect = 1 << 4, // odłączanie wagonów (bez skanowania sygnalizacji)
// jazda
Shunt = 1 << 5, // tryb manewrowy
Loose_shunt = 1 << 6, // coupling-free shunting mode
Obey_train = 1 << 7, // tryb pociągowy
Bank = 1 << 8, // assist mode
// others
Jump_to_first_order = 1 << 9, // zapęlenie do pierwszej pozycji (po co?)
};
// TSignals is shifted into TMovementStatus
enum TSignals
{
Signal_START = 30,
Signal_Pc6 = 30,
Signal_A1 = 31,
Signal_MAX = 31
};
enum TMovementStatus
{ // flagi bitowe ruchu (iDrivigFlags)
moveStopCloser = 1, // podjechać blisko W4 (nie podjeżdżać na początku ani po zmianie czoła)
moveStopPoint = 2, // stawać na W4 (wyłączone podczas zmiany czoła)
moveActive = 4, // pojazd jest załączony i skanuje
movePress = 8, // dociskanie przy odłączeniu (zamiast zmiennej Prepare2press)
moveConnect = 0x10, // jest blisko innego pojazdu i można próbować podłączyć
movePrimary = 0x20, // ma priorytet w składzie (master)
moveLate = 0x40, // flaga spóźnienia, włączy bardziej
moveStopHere = 0x80, // nie podjeżdżać do semafora, jeśli droga nie jest wolna
moveStartHorn = 0x100, // podawaj sygnał po podaniu wolnej drogi
moveStartHornNow = 0x200, // podaj sygnał po odhamowaniu
moveStartHornDone = 0x400, // podano sygnał po podaniu wolnej drogi
moveOerlikons = 0x800, // skład wyłącznie z zaworami? Oerlikona
moveIncSpeed = 0x1000, // załączenie jazdy (np. dla EZT)
moveTrackEnd = 0x2000, // dalsza jazda do przodu trwale ograniczona (W5, koniec toru)
moveSwitchFound = 0x4000, // na drodze skanowania do przodu jest rozjazd
moveGuardSignal = 0x8000, // sygnał od kierownika (minął czas postoju)
moveVisibility = 0x10000, // jazda na widoczność po przejechaniu S1 na SBL
moveDoorOpened = 0x20000, // drzwi zostały otwarte - doliczyć czas na zamknięcie
movePushPull = 0x40000, // zmiana czoła przez zmianę kabiny - nie odczepiać przy zmianie kierunku
moveSignalFound = 0x80000, // na drodze skanowania został znaleziony semafor
moveStopPointFound = 0x100000, // stop point detected ahead
moveGuardOpenDoor = 0x200000, // time for opening a door before departure
moveDepartureWarned = 0x400000 // driver has already warned about the doors being closed (only for manual warning - i.e. EN57-ish)
/*
moveSemaphorWasElapsed = 0x100000, // minięty został semafor
moveTrainInsideStation = 0x200000, // pociąg między semaforem a rozjazdami lub następnym semaforem
moveSpeedLimitFound = 0x400000 // pociąg w ograniczeniu z podaną jego długością
*/
};
enum TStopReason
{ // powód zatrzymania, dodawany do SetVelocity 0 - w zasadzie do usunięcia
stopNone, // nie ma powodu - powinien jechać
stopSleep, // nie został odpalony, to nie pojedzie
stopSem, // semafor zamknięty
stopTime, // czekanie na godzinę odjazdu
stopEnd, // brak dalszej części toru
stopDir, // trzeba stanąć, by zmienić kierunek jazdy
stopJoin, // stoi w celu połączenia wagonów
stopBlock, // przeszkoda na drodze ruchu
stopComm, // otrzymano taką komendę (niewiadomego pochodzenia)
stopOut, // komenda wyjazdu poza stację (raczej nie powinna zatrzymywać!)
stopRadio, // komunikat przekazany radiem (Radiostop)
stopExt, // komenda z zewnątrz
stopError // z powodu błędu w obliczeniu drogi hamowania
};
enum class TAction : int
{ // przechowanie aktualnego stanu AI od poprzedniego przebłysku świadomości
actUnknown, // stan nieznany (domyślny na początku)
actPantUp, // podnieś pantograf (info dla użytkownika)
actConv, // załącz przetwornicę (info dla użytkownika)
actCompr, // załącz sprężarkę (info dla użytkownika)
actSleep, //śpi (wygaszony)
actDrive, // jazda
actGo, // ruszanie z miejsca
actSlow, // przyhamowanie przed ograniczeniem
sctStop, // hamowanie w celu precyzyjnego zatrzymania
actIdle, // luzowanie składu przed odjazdem
actRelease, // luzowanie składu po zmniejszeniu prędkości
actConnect, // dojazd w celu podczepienia
actWait, // czekanie na przystanku
actReady, // zgłoszona gotowość do odjazdu od kierownika
actEmergency, // hamowanie awaryjne
actGoUphill, // ruszanie pod górę
actTest, // hamowanie kontrolne (podczas jazdy)
actTrial // próba hamulca (na postoju)
};
enum TSpeedPosFlag
{ // wartości dla iFlag w TSpeedPos
spNone = 0x0,
spEnabled = 0x1, // pozycja brana pod uwagę
spTrack = 0x2, // to jest tor
spReverse = 0x4, // odwrotnie
spSwitch = 0x8, // to zwrotnica
spSwitchStatus = 0x10, // stan zwrotnicy
spElapsed = 0x20, // pozycja minięta przez pojazd
spEnd = 0x40, // koniec
spCurve = 0x80, // łuk
spEvent = 0x100, // event
spShuntSemaphor = 0x200, // tarcza manewrowa
spPassengerStopPoint = 0x400, // przystanek osobowy (wskaźnik W4)
spStopOnSBL = 0x800, // zatrzymanie na SBL
spCommandSent = 0x1000, // komenda wysłana
spOutsideStation = 0x2000, // wskaźnik końca manewrów
spSemaphor = 0x4000, // semafor pociągowy
spRoadVel = 0x8000, // zadanie prędkości drogowej
spSectionVel = 0x20000, // odcinek z ograniczeniem
spProximityVelocity = 0x40000, // odcinek z ograniczeniem i podaną jego długościa
spNotOurShuntSignal = 0x80000 // shunting signal a dormant driver found already permitting - not for us
// spDontApplySpeedLimit = 0x10000 // this point won't apply its speed limit. potentially set by the scanning vehicle
};
// names of speed table entry flags, for the debug panel; spEnabled is skipped, every visible entry has it
struct TSpeedPosFlagDescription {
int flag;
char const *name;
};
inline constexpr TSpeedPosFlagDescription SpeedPosFlagDescriptions[] {
{ spSemaphor, "semaphore" },
{ spShuntSemaphor, "shunt" },
{ spPassengerStopPoint, "W4" },
{ spOutsideStation, "W5" },
{ spSwitch, "switch" },
{ spSwitchStatus, "diverging" },
{ spTrack, "track" },
{ spCurve, "curve" },
{ spEnd, "end" },
{ spSectionVel, "section" },
{ spProximityVelocity, "proximity" },
{ spRoadVel, "road" },
{ spStopOnSBL, "SBL" },
{ spReverse, "reverse" },
{ spElapsed, "passed" },
{ spCommandSent, "sent" },
{ spNotOurShuntSignal, "not-ours" } };
class TSpeedPos
{ // pozycja tabeli prędkości dla AI
public:
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
int iFlags{ spNone }; // flagi typu wpisu do tabelki
bool bMoved{ false }; // czy przesunięty (dotyczy punktu zatrzymania w peronie)
double fMoved{ 0.0 }; // ile przesunięty (dotyczy punktu zatrzymania w peronie)
glm::dvec3 vPos; // współrzędne XYZ do liczenia odległości
struct
{
TTrack *trTrack{ nullptr }; // wskaźnik na tor o zmiennej prędkości (zwrotnica, obrotnica)
basic_event *evEvent{ nullptr }; // połączenie z eventem albo komórką pamięci
};
void CommandCheck();
public:
TSpeedPos(TTrack *track, double dist, int flag);
TSpeedPos(basic_event *event, double dist, double length, TOrders order);
TSpeedPos() = default;
void Clear();
bool Update();
// aktualizuje odległość we wpisie
inline
void
UpdateDistance( double dist ) {
fDist -= dist; }
bool Set(basic_event *e, double d, double length, TOrders order = Wait_for_orders);
void Set(TTrack *t, double d, int f);
std::string TableText() const;
std::string GetName() const;
bool IsProperSemaphor(TOrders order = Wait_for_orders);
// signal ahead permits - wakes a dormant AI without waiting for the next change
bool AllowsDeparture() const;
};
//----------------------------------------------------------------------------
static const bool Aggressive = true;
static const bool Easyman = false;
static const bool AIdriver = true;
static const bool Humandriver = false;
static const int maxorders = 64; // ilość rozkazów w tabelce
static const int maxdriverfails = 4; // ile błędów może zrobić AI zanim zmieni nastawienie
extern bool WriteLogFlag; // logowanie parametrów fizycznych
static const int BrakeAccTableSize = 20;
static const int gbh_NP = -2; //odciecie w hamulcu ogolnym
static const int gbh_RP = 0; //jazda w hamulcu ogolnym
static const int gbh_FS = -1; //napelnianie uderzeniowe w hamulcu ogolnym
static const int gbh_MIN = -2; //minimalna pozycja w hamulcu ogolnym
static const int gbh_MAX = 6; //maksymalna pozycja w hamulcu ogolnym
//----------------------------------------------------------------------------
class TController {
// TBD: few authorized inspectors, or bunch of public getters?
friend class TTrain;
friend class drivingaid_panel;
friend class timetable_panel;
friend class scenario_panel;
friend class debug_panel;
friend class whois_event;
public:
TController( bool AI, TDynamicObject *NewControll, bool InitPsyche, bool primary = true );
~TController();
// ai operations logic
// types
using hintpredicate = std::function<bool(float)>; // returns true if action hint can be removed
// methods
public:
void Update( double dt ); // uruchamiac przynajmniej raz na sekundę
void MoveTo( TDynamicObject *to );
void TakeControl( bool const Aidriver, bool const Forcevehiclecheck = false );
void ReleaseTransientControls(); // clears the warning signal and the sandbox on a change of primary
bool primary( bool const Primary );
inline
bool primary() const {
return (iDrivigFlags & movePrimary) != 0; };
// drops primary from the others in the consist and takes the lead (valve / scan table)
void ClaimConsistPrimary();
// a primary with iEngineActive in the consist; nullptr = free to take the lead
TController *ActiveEnginePrimary() const;
inline
TMoverParameters const *Controlling() const {
return mvControlling; }
inline
TMoverParameters const *Occupied() const {
return mvOccupied; }
void DirectionInitial();
void DirectionChange();
inline
int Direction() const {
return iDirection; }
inline
TAction & action() {
return eAction; }
inline
TAction const & action() const {
return eAction; }
inline
bool is_active() const {
return TestFlag( iDrivigFlags, moveActive ); }
inline
bool is_train() const {
return TestFlag( mvOccupied->CategoryFlag, 1 ); }
inline
bool is_car() const {
return TestFlag( mvOccupied->CategoryFlag, 2 ); }
inline
bool is_emu() const {
return mvControlling->TrainType == dt_EZT; }
inline
bool is_dmu() const {
return mvControlling->TrainType == dt_DMU; }
inline
bool has_diesel_engine() const {
return mvControlling->EngineType == TEngineType::DieselElectric || mvControlling->EngineType == TEngineType::DieselEngine;
}
TBrakeSystem consist_brake_system() const;
private:
void Activation(); // umieszczenie obsady w odpowiednim członie
void ControllingSet(); // znajduje człon do sterowania
void SetDriverPsyche();
bool IncBrake();
bool DecBrake();
void LapBrake();
void ZeroLocalBrake();
bool IncSpeed();
bool DecSpeed( bool force = false );
void ZeroSpeed( bool const Enforce = false );
bool IncBrakeEIM();
bool DecBrakeEIM();
bool IncSpeedEIM();
bool DecSpeedEIM( int const Amount = 1 );
void BrakeLevelSet( double b );
bool BrakeLevelAdd( double b );
void SpeedSet();
void SpeedCntrl( double DesiredSpeed );
void SetTimeControllers(); /*setting state of time controllers depending of desired action*/
void CheckTimeControllers(); /*checking state of time controllers to reset them to stable position*/
double ESMVelocity( bool Main );
void PrepareDirection();
bool PrepareHeating();
// 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 );
void ZeroDirection();
int OrderDirectionChange( int newdir, TMoverParameters *Vehicle );
void sync_consist_reversers();
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; };
inline
double DirectionalVel() const {
return mvOccupied->Vel * sign( iDirection * mvOccupied->V ); }
void update_timers( double const dt );
void update_logs( double const dt );
void determine_consist_state();
void determine_braking_distance();
void determine_proximity_ranges();
void scan_route( double const Range );
void ScanForWakeup( double const Range ); // dormant/secondary AI: checks the signal ahead for a wake-up
// front/rear/fLength per CheckDirection, CabOccupied fallback; no CheckVehicles side effects
void OrientScanToCab();
void scan_obstacles( double const Range );
void UpdateSecondary( double const Awarenessrange ); // scans for a wake-up, does not drive
void SettleSecondaryAI(); // drops force and brake, keeps vigilance quiet
void control_wheelslip();
void control_pantographs();
void control_horns( double const Timedelta );
void control_security_system( double const Timedelta );
void control_handles();
void control_lights();
void control_compartment_lights();
void control_doors();
void UpdateCommand();
void handle_engine();
void handle_orders();
void UpdateNextStop();
void check_load_exchange(); // returns: estimated remaining time of load exchange, in seconds
void check_departure();
void UpdateConnect();
void UpdateDisconnect();
int unit_count( int const Threshold ) const;
void UpdateChangeDirection();
void UpdateLooseShunt();
void UpdateObeyTrain();
void GuardOpenDoor();
void pick_optimal_speed( double const Range );
void adjust_desired_speed_for_obstacles();
void adjust_desired_speed_for_limits();
void adjust_desired_speed_for_target_speed( double const Range );
void adjust_desired_speed_for_current_speed();
void adjust_desired_speed_for_braking_test();
void control_tractive_and_braking_force();
void control_releaser();
void control_main_pipe();
void control_relays();
void control_motor_connectors();
void control_tractive_force();
void increase_tractive_force();
void control_braking_force();
void apply_independent_brake_only();
void check_route_ahead( double const Range );
// the driver at the front (scanning direction) - the signal hands the lead to them
TController *DepartureDriver() const;
// engines down: primary for the front; running: the order goes to the current primary
void WakeForDeparture( std::string const &Command, double const Value1, double const Value2 );
// false when the permission at the nearest signal was given to another consist
bool SignalPermissionIsOurs( bool const Signalgivesgo ) const;
bool OtherConsistAtSignal() const; // another consist in front of the signal = the addressee of the permission
void check_route_behind( double const Range );
void UpdateBrakingHelper();
void hint( driver_hint const Value, hintpredicate const Predicate, float const Predicateparameter = 0.f );
void update_hints();
void remove_hint( driver_hint const Value );
void remove_train_brake_hints();
void remove_master_controller_hints();
void remove_reverser_hints();
void cue_action( driver_hint const Action, float const Actionparameter = 0.f );
// members
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
int UniversalBrakeButtons = 0.0; // flag of which universal buttons need to be pressed
int DizelPercentage = 0; // oczekiwane procenty jazdy/hamowania szynobusem
int DizelPercentage_Speed = 0; // oczekiwane procenty jazdy/hamowania szynobusem w związku z osiąganiem VelDesired
double fMedAmax = 0.8; //maximum decceleration when using ep/med brake
private:
bool Psyche = false;
int HelperState = 0; //stan pomocnika maszynisty
TDynamicObject *pVehicle = nullptr; // pojazd w którym siedzi sterujący
TMoverParameters *mvControlling = nullptr; // jakim pojazdem steruje (może silnikowym w EZT)
TMoverParameters *mvOccupied = nullptr; // jakim pojazdem hamuje
TMoverParameters *mvPantographUnit = nullptr; //pantograph equipped vehicle in the occupied/controlled set
std::string VehicleName;
std::array<int, 2> m_lighthints = { -1, -1 }; // suggested light patterns
double AccPreferred = 0.0; // preferowane przyspieszenie (wg psychiki kierującego, zmniejszana przy wykryciu kolizji)
double AccDesired = 0.0; // 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
double fAccDesiredAv = 0.0; // uśrednione przyspieszenie z kolejnych przebłysków świadomości, żeby ograniczyć migotanie
double VelforDriver = -1.0; // prędkość, używana przy zmianie kierunku (ograniczenie przy nieznajmości szlaku?)
double VelSignal = 0.0; // ograniczenie prędkości z kompilacji znaków i sygnałów // normalnie na początku ma stać, no chyba że jedzie
double VelLimit = -1.0; // predkość zadawana przez event jednokierunkowego ograniczenia prędkości // -1: brak ograniczenia prędkości
double VelSignalLast = -1.0; // prędkość zadana na ostatnim semaforze // ostatni semafor też bez ograniczenia
double VelSignalNext = 0.0; // prędkość zadana na następnym semaforze
double VelLimitLast = -1.0; // prędkość zadana przez ograniczenie // ostatnie ograniczenie bez ograniczenia
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
double FirstSemaphorDist = 10000.0; // odległość do pierwszego znalezionego semafora
double ActualProximityDist = 1.0; // odległość brana pod uwagę przy wyliczaniu prędkości i przyspieszenia
std::pair<double, double> VelLimitLastDist { 0.0, 0.0 }; // distance to velocity change point
double SwitchClearDist { 0.0 }; // distance to point after farthest detected switch
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)
std::optional<int> iDirectionBackup; // consist direction to be restored after coupling/uncoupling and similar direction-changing operations
std::optional<std::pair<TDynamicObject*,int>> iCouplingVehicle; // <vehicle, coupler> to be coupled with another consist in current coupling operation
int iVehicleCount = -2; // wartość neutralna // ilość pojazdów do odłączenia albo zabrania ze składu (-1=wszystkie)
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 IsHeatingTemperatureOK{ true };
bool IsHeatingTemperatureTooLow{ false };
bool IsAtPassengerStop{ false }; // true if the consist is within acceptable range of w4 post
bool IsScheduledPassengerStopVisible{ false };
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)
double fVoltage = 0.0; // uśrednione napięcie sieci: przy spadku poniżej wartości minimalnej opóźnić rozruch o losowy czas
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
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
double fStopTime = 0.0; // czas postoju przed dalszą jazdą (np. na przystanku)
float ExchangeTime{ 0.0 }; // time needed to finish current load exchange
double fShuntVelocity = 40.0; // prędkość manewrowania, zależy m.in. od składu
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 = 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
TBrakeSystem BrakeSystem = TBrakeSystem::Individual; //type of main brake
bool ForcePNBrake = false; //is it necessary to use PN brake instead of EP brake
int DynamicBrakeTest = 0; //is it necessary to make brake test while driving
double DBT_VelocityBrake = 0;
double DBT_VelocityRelease = 0;
double DBT_VelocityFinish = 0;
double DBT_BrakingTime = 0;
double DBT_ReleasingTime = 0;
double DBT_MidPointAcc = 0;
int StaticBrakeTest = 0; //is it necessary to make brake test while standing
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
std::list< std::tuple<driver_hint, hintpredicate, float> > m_hints; // queued ai operations
// 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 );
// defines assignment data
inline auto assignment() -> std::string & { return m_assignment; }
inline auto assignment() const -> std::string const & { return m_assignment; }
std::string OrderCurrent() const;
private:
void RecognizeCommand(); // odczytuje komende przekazana lokomotywie
// whether the command wakes a secondary crew up to lead (a driving or startup order)
static bool CommandClaimsPrimary( std::string const &Command, double const Value );
void JumpToNextOrder( bool const Skipmergedchangedirection = false );
void JumpToFirstOrder();
void OrderPush( TOrders NewOrder );
void OrderNext( TOrders NewOrder );
inline TOrders OrderCurrentGet() const;
inline TOrders OrderNextGet() const;
void OrderCheck();
void OrdersInit( double fVel );
void OrdersClear();
void OrdersDump( std::string const Neworder, bool const Verbose = true );
std::string Order2Str( TOrders Order ) const;
// members
std::string m_assignment;
glm::dvec3 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<basic_event *> CheckTrackEvent( TTrack *Track, double const fDirection ) const;
bool TableAddNew();
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 );
bool TableUpdateStopPoint( TCommandType &Command, TSpeedPos &Point, double const Signaldistance );
bool TableUpdateEvent( double &Velocity, TCommandType &Command, TSpeedPos &Point, double &Signaldistance, int const Pointindex );
// returns most recently calculated distance to potential obstacle ahead
double TrackObstacle() const;
void TablePurger();
void TableSort();
inline double MoveDistanceGet() const {
return dMoveLen;
}
inline void MoveDistanceReset() {
dMoveLen = 0.0;
}
std::size_t TableSize() const { return sSpeedTable.size(); }
void TableClear();
int TableDirection() { return iTableDirection; }
// Ra: stare funkcje skanujące, używane do szukania sygnalizatora z tyłu
bool IsOccupiedByAnotherConsist( TTrack *Track, double const Distance = 0.0 ) const;
// distance to the nearest vehicle of another consist ahead on the track; -1.0 when there is none
double DistanceToAnotherConsist( TTrack *Track, double const Distance ) const;
bool BelongsToConsist( TDynamicObject const *Vehicle ) const; // whether the vehicle is part of our consist
basic_event *CheckTrackEventBackward( double fDirection, TTrack *Track, TDynamicObject *Vehicle, int const Eventdirection = 1, end const End = end::rear );
TTrack *BackwardTraceRoute( double &fDistance, double &fDirection, TDynamicObject *Vehicle, basic_event *&Event, int const Eventdirection = 1, end const End = end::rear, bool const Untiloccupied = true );
void SetProximityVelocity( double dist, double vel, glm::dvec3 const *pos );
TCommandType BackwardScan( double const Range );
std::string TableText( std::size_t const Index ) const;
double TableDistance( std::size_t const Index ) const; // distance of the entry; max when out of range
// scan table row describing the nearest vehicle on the route; empty when the way is clear
std::string ObstacleText() const;
/*
void RouteSwitch(int d);
*/
// members
int iLast{ 0 }; // ostatnia wypełniona pozycja w tabeli <iFirst (modulo iSpeedTableSize)
int iTableDirection{ 0 }; // kierunek zapełnienia tabelki względem pojazdu z AI
std::vector<TSpeedPos> 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 ) };
// entry that produced the permitting command; SemNextIndex can point at a different one
std::size_t SemPermitIndex{ 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]
neighbour_data Obstacle; // nearest vehicle detected ahead on current route
// timetable
// methods
public:
const std::string &TrainName() const;
Mtable::TTrainParameters const & TrainTimetable() const;
private:
std::string Relation() const;
int StationIndex() const;
int StationCount() const;
bool IsStop() const;
std::string NextStop() const;
// tests whether the train is delayed and sets accordingly a driving flag
void UpdateDelayFlag();
// members
Mtable::TTrainParameters TrainParams; // 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
std::string m_lastexchangestop; // HACK: safeguard to prevent multiple load exchanges per station
int m_lastexchangeplatforms { 0 }; // cached station platforms for last exchange
int m_lastexchangedirection { 0 }; //
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 };
announcement_t m_lastannouncement{ announcement_t::idle };
bool m_makenextstopannouncement{ false };
// consist
// methods
public:
bool CheckVehicles( TOrders user = Wait_for_orders );
private:
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;
bool doors_permit_active() const;
void AutoRewident(); // ustawia hamulce w składzie
void announce( announcement_t const Announcement );
// 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 ControlledEnginesCount{ 0 }; // number of powered vehicles under driver's control
bool iEngineActive{ false }; // ABu: Czy silnik byl juz zalaczony
bool IsPassengerTrain{ false };
bool IsCargoTrain{ false };
bool IsHeavyCargoTrain{ false };
double fReady = 0.0; // poziom odhamowania wagonów
bool Ready = false; // ABu: stan gotowosci do odjazdu - sprawdzenie odhamowania wagonow
bool IsConsistBraked { false };
double ConsistShade{ 1.0 }; // averaged amount of sunlight received by the consist
TDynamicObject *pVehicles[ 2 ]; // skrajne pojazdy w składzie (niekoniecznie bezpośrednio sterowane)
bool DoesAnyDoorNeedOpening{ false };
bool IsAnyDoorOpen[ 2 ]; // state of door in the consist
bool IsAnyDoorOnlyOpen[ 2 ]; // state of door in the consist regardless of step
bool IsAnyDoorPermitActive[ 2 ]; // state of door permit in the consist
bool IsAnyLineBreakerOpen{ false }; // state of line breaker in all powered vehicles under control
bool IsAnyConverterOverloadRelayOpen{ false }; // state of converter overload relays in all vehicles under control
bool IsAnyMotorOverloadRelayOpen{ false }; // state of motor overload relays in all vehicles under control
bool IsAnyGroundRelayOpen{ false };
bool IsAnyCompressorPresent { false };
bool IsAnyCompressorEnabled{ false };
bool IsAnyCompressorExplicitlyEnabled{ false }; // only takes into account manually controlled devices
bool IsAnyConverterPresent{ false };
bool IsAnyConverterEnabled{ false };
bool IsAnyConverterExplicitlyEnabled{ false }; // only takes into account manually controlled devices
bool IsAnyCouplerStretched{ false }; // whether there's a coupler in the consist stretched above limit
// 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( end 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
*/
};
inline TOrders TController::OrderCurrentGet() const {
return OrderList[ OrderPos ];
}
inline TOrders TController::OrderNextGet() const {
return OrderList[ OrderPos + 1 ];
}