mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-29 01:07:28 +01:00
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.
This commit is contained in:
@@ -339,6 +339,9 @@ bool TSpeedPos::Update()
|
||||
// odczyt komórki pamięci najlepiej by było zrobić jako notyfikację,
|
||||
// czyli zmiana komórki wywoła jakąś podaną funkcję
|
||||
CommandCheck(); // sprawdzenie typu komendy w evencie i określenie prędkości
|
||||
if( fVelNext == 0.0 ) {
|
||||
iFlags &= ~spNotOurShuntSignal; // saw "stop" - the next permission will light up with us here
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -359,13 +362,24 @@ std::string TSpeedPos::GetName() const
|
||||
}
|
||||
|
||||
std::string TSpeedPos::TableText() const
|
||||
{ // pozycja tabelki pr?dko?ci
|
||||
if (iFlags & spEnabled)
|
||||
{ // o ile pozycja istotna
|
||||
return to_hex_str(iFlags, 8) + " " + to_string(fDist, 1, 6) +
|
||||
" " + (fVelNext == -1.0 ? " -" : to_string(static_cast<int>(fVelNext), 0, 3)) + " " + GetName();
|
||||
{ // one speed table entry - distance/velocity/name plus readable flag tags instead of hex
|
||||
if( ( iFlags & spEnabled ) == 0 ) {
|
||||
return ""; // disabled - empty string, the F2 panel skips such an entry
|
||||
}
|
||||
return "Empty";
|
||||
|
||||
std::string tags;
|
||||
for( auto const &description : SpeedPosFlagDescriptions ) {
|
||||
if( iFlags & description.flag ) {
|
||||
if( false == tags.empty() ) { tags += " "; }
|
||||
tags += description.name;
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
to_string( fDist, 1, 8 )
|
||||
+ " " + ( fVelNext == -1.0 ? " -" : to_string( static_cast<int>( fVelNext ), 0, 3 ) )
|
||||
+ " " + GetName()
|
||||
+ ( tags.empty() ? "" : " [" + tags + "]" );
|
||||
}
|
||||
|
||||
bool TSpeedPos::IsProperSemaphor(TOrders order)
|
||||
@@ -385,6 +399,14 @@ bool TSpeedPos::IsProperSemaphor(TOrders order)
|
||||
return false; // true gdy zatrzymanie, wtedy nie ma po co skanować dalej
|
||||
}
|
||||
|
||||
bool TSpeedPos::AllowsDeparture() const
|
||||
{ // signal ahead permits - a dormant AI may start without waiting for a change
|
||||
if( fDist <= 0.0 ) { return false; }
|
||||
if( ( iFlags & spEnabled ) == 0 ) { return false; }
|
||||
if( ( iFlags & ( spSemaphor | spShuntSemaphor ) ) == 0 ) { return false; }
|
||||
return fVelNext < 0.0 || fVelNext >= 1.0;
|
||||
}
|
||||
|
||||
bool TSpeedPos::Set(basic_event *event, double dist, double length, TOrders order)
|
||||
{ // zapamiętanie zdarzenia
|
||||
fDist = dist;
|
||||
@@ -398,6 +420,11 @@ bool TSpeedPos::Set(basic_event *event, double dist, double length, TOrders orde
|
||||
}
|
||||
iFlags |= spEnabled;
|
||||
CommandCheck(); // sprawdzenie typu komendy w evencie i określenie prędkości
|
||||
if( ( iFlags & spShuntSemaphor ) != 0
|
||||
&& fVelNext != 0.0 ) {
|
||||
// a shunting signal addresses nobody in particular; a semaphore sets a route for us
|
||||
iFlags |= spNotOurShuntSignal; // permission found already lit - we did not see who it was given to
|
||||
}
|
||||
// zależnie od trybu sprawdzenie czy jest tutaj gdzieś semafor lub tarcza manewrowa
|
||||
// jeśli wskazuje stop wtedy wystawiamy true jako koniec sprawdzania
|
||||
// WriteLog("EventSet: Vel=" + AnsiString(fVelNext) + " iFlags=" + AnsiString(iFlags) + " order="+AnsiString(order));
|
||||
@@ -451,6 +478,7 @@ void TController::TableClear()
|
||||
fLastVel = -1.0;
|
||||
SemNextIndex = -1;
|
||||
SemNextStopIndex = -1;
|
||||
SemPermitIndex = std::size_t( -1 );
|
||||
eSignSkip = nullptr; // nic nie pomijamy
|
||||
};
|
||||
|
||||
@@ -503,7 +531,10 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle)
|
||||
double fCurrentDistance{ 0.0 }; // aktualna przeskanowana długość
|
||||
double fLastDir{ 0.0 };
|
||||
|
||||
if (iTableDirection != iDirection ) {
|
||||
// with no direction set (dormant AI) we scan by the occupied cab
|
||||
int const scandirection{ iDirection != 0 ? iDirection : mvOccupied->CabOccupied };
|
||||
|
||||
if (iTableDirection != scandirection ) {
|
||||
// jeśli zmiana kierunku, zaczynamy od toru ze wskazanym pojazdem
|
||||
TableClear();
|
||||
/*
|
||||
@@ -519,10 +550,19 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle)
|
||||
// don't allow potential red light overrun keep us from reversing
|
||||
VelSignalLast = -1.0;
|
||||
}
|
||||
iTableDirection = iDirection; // ustalenie w jakim kierunku jest wypełniana tabelka względem pojazdu
|
||||
iTableDirection = scandirection;
|
||||
pTrack = pVehicle->GetTrack(); // odcinek, na którym stoi
|
||||
fTrackLength = pVehicle->RaTranslationGet(); // pozycja na tym torze (odległość od Point1)
|
||||
fLastDir = pVehicle->DirectionGet() * pVehicle->RaDirectionGet(); // ustalenie kierunku skanowania na torze
|
||||
if( pVehicles[ end::front ] != nullptr
|
||||
&& pVehicles[ end::front ] != pVehicle ) {
|
||||
// driver not in the front vehicle: DirectionGet() may point backwards relative to the consist
|
||||
auto const towardfront{ pVehicles[ end::front ]->GetPosition() - pVehicle->GetPosition() };
|
||||
auto const nose{ pVehicle->VectorFront() * static_cast<double>( pVehicle->DirectionGet() ) };
|
||||
if( towardfront.x * nose.x + towardfront.z * nose.z < 0.0 ) {
|
||||
fLastDir = -fLastDir;
|
||||
}
|
||||
}
|
||||
if( fLastDir < 0.0 ) {
|
||||
// jeśli w kierunku Point2 toru
|
||||
fTrackLength = pTrack->Length() - fTrackLength; // przeskanowana zostanie odległość do Point2
|
||||
@@ -635,6 +675,10 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle)
|
||||
+ ")");
|
||||
}
|
||||
}
|
||||
if( iEngineActive ) {
|
||||
// only a dormant driver acts on it, so do not tag it for anybody else
|
||||
newspeedpoint.iFlags &= ~spNotOurShuntSignal;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // event dodajemy najpierw, żeby móc sprawdzić, czy tor został dodany po odczytaniu prędkości następnego
|
||||
@@ -700,6 +744,16 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle)
|
||||
}
|
||||
}
|
||||
|
||||
auto const distancetotrackend{ fCurrentDistance + fTrackLength };
|
||||
// sign of the distance encodes the scanning direction (fLastDir < 0 = towards Point2)
|
||||
if( distancetotrackend > 0.0 // the track still reaches ahead of us
|
||||
&& IsOccupiedByAnotherConsist( pTrack, distancetotrackend * ( fLastDir < 0.0 ? 1.0 : -1.0 ) ) ) {
|
||||
// another consist stands here: it owns the signals beyond it, so we scan no further. The track
|
||||
// stays in the table, marked checked - scanning resumes from the last entry, no dup control
|
||||
tLast = pTrack;
|
||||
break;
|
||||
}
|
||||
|
||||
fCurrentDistance += fTrackLength; // doliczenie kolejnego odcinka do przeskanowanej długości
|
||||
tLast = pTrack; // odhaczenie, że sprawdzony
|
||||
fLastVel = pTrack->VelocityGet(); // prędkość na poprzednio sprawdzonym odcinku
|
||||
@@ -772,7 +826,9 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle)
|
||||
|
||||
void TController::TableCheck(double fDistance)
|
||||
{ // przeliczenie odległości w tabelce, ewentualnie doskanowanie (bez analizy prędkości itp.)
|
||||
if( iTableDirection != iDirection ) {
|
||||
// as in TableTraceRoute: a dormant AI has no iDirection, so we orient by the cab
|
||||
int const scandirection{ iDirection != 0 ? iDirection : mvOccupied->CabOccupied };
|
||||
if( iTableDirection != scandirection ) {
|
||||
// jak zmiana kierunku, to skanujemy od końca składu
|
||||
TableTraceRoute( fDistance, pVehicles[ end::rear ] );
|
||||
TableSort();
|
||||
@@ -859,6 +915,7 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
|
||||
auto d { 0.0 }; // droga
|
||||
auto d_to_next_sem { 10000.0 }; //ustaiwamy na pewno dalej niż widzi AI
|
||||
auto go { TCommandType::cm_Unknown };
|
||||
SemPermitIndex = std::size_t( -1 ); // filled in by whichever point yields a permitting command
|
||||
auto speedlimitiscontinuous { true }; // stays true if potential active speed limit is unbroken to the last (relevant) point in scan table
|
||||
eSignNext = nullptr;
|
||||
IsAtPassengerStop = false;
|
||||
@@ -1546,6 +1603,13 @@ TController::TableUpdateEvent( double &Velocity, TCommandType &Command, TSpeedPo
|
||||
if( isforsomeoneelse ) {
|
||||
VelSignalNext = 0.0;
|
||||
Velocity = 0.0;
|
||||
// it permits, but for the vehicle standing in front of us. Record that now -
|
||||
// once it pulls away the signal alone no longer says whose permission it was
|
||||
if( false == iEngineActive
|
||||
&& ( Point.iFlags & spShuntSemaphor )
|
||||
&& Point.fVelNext != 0.0 ) {
|
||||
Point.iFlags |= spNotOurShuntSignal;
|
||||
}
|
||||
}
|
||||
else {
|
||||
VelSignalNext = Point.fVelNext;
|
||||
@@ -1553,6 +1617,16 @@ TController::TableUpdateEvent( double &Velocity, TCommandType &Command, TSpeedPo
|
||||
Velocity = fVelMax;
|
||||
VelSignal = fVelMax;
|
||||
}
|
||||
if( Command == TCommandType::cm_Unknown && Point.AllowsDeparture() ) {
|
||||
// already permitting as it entered the FOV - wake up without waiting for a change
|
||||
Command = ( Point.iFlags & spShuntSemaphor ) != 0 ?
|
||||
TCommandType::cm_ShuntVelocity :
|
||||
TCommandType::cm_SetVelocity;
|
||||
SemPermitIndex = Pointindex;
|
||||
if( std::fabs( VelSignal ) < 1.0 ) {
|
||||
VelSignal = Point.fVelNext < 0.0 ? -1.0 : Point.fVelNext;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1630,6 +1704,7 @@ TController::TableUpdateEvent( double &Velocity, TCommandType &Command, TSpeedPo
|
||||
if( Velocity != 0.0 ) {
|
||||
// jeśli nie było komendy wcześniej - pierwsza się liczy - ustawianie VelSignal
|
||||
Command = TCommandType::cm_ShuntVelocity; // w trybie pociągowym tylko jeśli włącza tryb manewrowy (v!=0.0)
|
||||
SemPermitIndex = Pointindex;
|
||||
// Ra 2014-06: (VelSignal) nie może być tu ustawiane, bo Tm może być daleko
|
||||
if( VelSignal == 0.0 ) {
|
||||
// aby stojący ruszył
|
||||
@@ -1653,6 +1728,7 @@ TController::TableUpdateEvent( double &Velocity, TCommandType &Command, TSpeedPo
|
||||
|| Velocity >= 1.0 ) {
|
||||
// bo wartość 0.1 służy do hamowania tylko
|
||||
Command = TCommandType::cm_SetVelocity; // może odjechać
|
||||
SemPermitIndex = Pointindex;
|
||||
// Ra 2014-06: (VelSignal) nie może być tu ustawiane, bo semafor może być daleko
|
||||
// VelSignal=v; //nie do końca tak, to jest druga prędkość; -1 nie wpisywać...
|
||||
if( VelSignal == 0.0 ) {
|
||||
@@ -1820,6 +1896,15 @@ void TController::TableSort() {
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
bool TController::primary( bool const Primary )
|
||||
{
|
||||
SetFlag( iDrivigFlags, Primary ? movePrimary : -movePrimary );
|
||||
if( mvOccupied != nullptr ) {
|
||||
mvOccupied->BrakeValveActive = Primary;
|
||||
}
|
||||
return primary();
|
||||
}
|
||||
|
||||
TController::TController(bool AI, TDynamicObject *NewControll, bool InitPsyche, bool Primary) :// czy ma aktywnie prowadzić?
|
||||
AIControllFlag( AI ), pVehicle( NewControll )
|
||||
{
|
||||
@@ -1870,9 +1955,7 @@ TController::TController(bool AI, TDynamicObject *NewControll, bool InitPsyche,
|
||||
// OrderValue=0;
|
||||
OrdersClear();
|
||||
|
||||
if( true == Primary ) {
|
||||
iDrivigFlags |= movePrimary; // aktywnie prowadzące pojazd
|
||||
}
|
||||
primary( Primary );
|
||||
|
||||
SetDriverPsyche(); // na końcu, bo wymaga ustawienia zmiennych
|
||||
TableClear();
|
||||
@@ -2035,6 +2118,7 @@ void TController::Activation()
|
||||
// odcięcie na zaworze maszynisty, FVel6 po drugiej stronie nie luzuje
|
||||
mvOccupied->BrakeLevelSet( mvOccupied->Handle->GetPos(bh_NP) ); // odcięcie na zaworze maszynisty
|
||||
BrakeLevelSet( mvOccupied->BrakeCtrlPos ); //ustawienie zmiennej GBH
|
||||
mvOccupied->BrakeValveActive = false;
|
||||
}
|
||||
// przejście AI na drugą stronę EN57, ET41 itp.
|
||||
// TODO: clean this up, there's lot of redundancy with TMoverParameters::ChangeCab() and TTrain::MoveToVehicle()
|
||||
@@ -2049,6 +2133,7 @@ void TController::Activation()
|
||||
if( targetvehicle->DirectionGet() != pVehicle->DirectionGet() ) {
|
||||
// jeśli są przeciwne do siebie to będziemy jechać w drugą stronę względem zasiedzianego pojazdu
|
||||
iDirection = -iDirection;
|
||||
iDirectionOrder = iDirection; // so that DirectionChange does not flip it back
|
||||
}
|
||||
// move the other driver to our old vehicle
|
||||
pVehicle->Mechanik = targetvehicledriver; // wsadzamy tego, co ewentualnie był (podwójna trakcja)
|
||||
@@ -2100,6 +2185,7 @@ void TController::Activation()
|
||||
}
|
||||
|
||||
CheckVehicles(); // sprawdzenie składu, AI zapali światła
|
||||
sync_consist_reversers(); // after a cab change the members' reversers have to agree
|
||||
TableClear(); // resetowanie tabelki skanowania torów
|
||||
}
|
||||
};
|
||||
@@ -2343,6 +2429,10 @@ int TController::CheckDirection() {
|
||||
// jeśli nie ma ustalonego kierunku to jedziemy wg aktualnej kabiny
|
||||
d = mvOccupied->CabActive;
|
||||
}
|
||||
if( !d ) {
|
||||
// dormant: reverser/cab inactive - orient by the occupied cab
|
||||
d = mvOccupied->CabOccupied;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
@@ -2368,14 +2458,14 @@ bool TController::CheckVehicles(TOrders user)
|
||||
{ // sprawdzanie, czy jest głównym sterującym, żeby nie było konfliktu
|
||||
if( p->Mechanik // jeśli ma obsadę
|
||||
&& p->Mechanik != this ) { // ale chodzi o inny pojazd, niż aktualnie sprawdzający
|
||||
if( p->Mechanik->iDrivigFlags & movePrimary ) {
|
||||
if( p->Mechanik->primary() ) {
|
||||
// a tamten ma priorytet
|
||||
// TODO: take into account drivers' operating modes, one or more of them might be on banking duty
|
||||
if( iDrivigFlags & movePrimary
|
||||
if( primary()
|
||||
&& mvOccupied->DirAbsolute
|
||||
&& mvOccupied->BrakeCtrlPos >= -1 ) {
|
||||
// jeśli rządzi i ma kierunek
|
||||
p->Mechanik->primary( false ); // dezaktywuje tamtego
|
||||
p->Mechanik->primary( false );
|
||||
p->Mechanik->ZeroLocalBrake();
|
||||
p->MoverParameters->BrakeLevelSet( p->MoverParameters->Handle->GetPos( bh_NP ) ); // odcięcie na zaworze maszynisty
|
||||
p->Mechanik->BrakeLevelSet( p->MoverParameters->BrakeCtrlPos ); //ustawienie zmiennej GBH
|
||||
@@ -2398,12 +2488,12 @@ bool TController::CheckVehicles(TOrders user)
|
||||
p = p->Neighbour(dir); // pojazd podłączony od wskazanej strony
|
||||
}
|
||||
if( main ) {
|
||||
iDrivigFlags |= movePrimary; // nie znaleziono innego, można się porządzić
|
||||
primary( true );
|
||||
}
|
||||
|
||||
ControllingSet(); // ustalenie członu do sterowania (może być inny niż zasiedziany)
|
||||
|
||||
if (iDrivigFlags & movePrimary)
|
||||
if( primary() )
|
||||
{ // jeśli jest aktywnie prowadzącym pojazd, może zrobić własny porządek
|
||||
auto pantmask = 1;
|
||||
p = pVehicles[end::front];
|
||||
@@ -2565,15 +2655,19 @@ void TController::Lights(int head, int rear)
|
||||
}
|
||||
|
||||
void TController::DirectionInitial()
|
||||
{ // ustawienie kierunku po wczytaniu trainset (może jechać na wstecznym
|
||||
mvOccupied->CabActivisationAuto(); // załączenie rozrządu (wirtualne kabiny)
|
||||
if (mvOccupied->Vel > EU07_AI_NOMOVEMENT)
|
||||
{ // jeśli na starcie jedzie
|
||||
iDirection = iDirectionOrder =
|
||||
mvOccupied->V > 0 ? 1 : -1; // początkowa prędkość wymusza kierunek jazdy
|
||||
DirectionForward(mvOccupied->V * mvOccupied->CabActive >= 0.0); // a dalej ustawienie nawrotnika
|
||||
{ // sets the direction after a trainset is loaded
|
||||
mvOccupied->CabActivisationAuto();
|
||||
if( mvOccupied->Vel >= EU07_AI_MOVEMENT ) {
|
||||
// actually moving - direction from the sign of V
|
||||
iDirection = iDirectionOrder = mvOccupied->V > 0 ? 1 : -1;
|
||||
DirectionForward( mvOccupied->V * mvOccupied->CabActive >= 0.0 );
|
||||
}
|
||||
CheckVehicles(); // sprawdzenie świateł oraz skrajnych pojazdów do skanowania
|
||||
else {
|
||||
// standing: reverser forward relative to the cab, not from the sign of a micro V
|
||||
DirectionForward( true );
|
||||
iDirection = iDirectionOrder = CheckDirection();
|
||||
}
|
||||
CheckVehicles();
|
||||
};
|
||||
|
||||
void TController::DirectionChange() {
|
||||
@@ -3977,7 +4071,7 @@ void TController::SpeedCntrl(double DesiredSpeed)
|
||||
void TController::SetTimeControllers()
|
||||
{
|
||||
// TBD, TODO: rework this method to use hint system and regardless of driver type
|
||||
if( false == AIControllFlag || 0 == mvOccupied->CabActive ) { return; }
|
||||
if( !primary() || !AIControllFlag || mvOccupied->CabActive == 0 ) { return; }
|
||||
|
||||
//1. Check the type of Main Brake Handle
|
||||
if( BrakeSystem == TBrakeSystem::Pneumatic || ForcePNBrake )
|
||||
@@ -4188,7 +4282,7 @@ void TController::SetTimeControllers()
|
||||
void TController::CheckTimeControllers()
|
||||
{
|
||||
// TODO: rework this method to use hint system and regardless of driver type
|
||||
if( false == AIControllFlag || 0 == mvControlling->CabActive ) { return; }
|
||||
if( !primary() || !AIControllFlag || mvControlling->CabActive == 0 ) { return; } // a secondary crew does not touch the controllers
|
||||
|
||||
//1. Check the type of Main Brake Handle
|
||||
if( BrakeSystem == TBrakeSystem::ElectroPneumatic && mvOccupied->Handle->TimeEP && !ForcePNBrake )
|
||||
@@ -4389,8 +4483,99 @@ void TController::PutCommand(std::string NewCommand, double NewValue1, double Ne
|
||||
mvOccupied->PutCommand(NewCommand, NewValue1, NewValue2, NewLocation);
|
||||
}
|
||||
|
||||
bool TController::CommandClaimsPrimary( std::string const &Command, double const Value )
|
||||
{ // a driving/startup order wakes a secondary crew up to lead, otherwise nobody moves the consist
|
||||
auto const isdrivingorder = [&Command]( std::initializer_list<char const *> const names ) {
|
||||
for( auto const *name : names ) {
|
||||
if( Command == name ) { return true; }
|
||||
}
|
||||
return false;
|
||||
};
|
||||
return
|
||||
Command.compare( 0, 10, "Timetable:" ) == 0
|
||||
|| ( Command == "SetVelocity" && Value != 0.0 )
|
||||
|| isdrivingorder( { "Prepare_engine", "ShuntVelocity", "Obey_train", "Bank", "Shunt", "Loose_shunt" } );
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// front-to-back walk like CheckVehicles(); Neighbour() flips the coupler side on reversed members
|
||||
template <typename Callable>
|
||||
void ForEachInConsist( TDynamicObject *Vehicle, Callable Function ) {
|
||||
if( Vehicle == nullptr ) { return; }
|
||||
int side { end::front };
|
||||
auto *vehicle { Vehicle->FirstFind( side ) };
|
||||
if( vehicle == nullptr ) { vehicle = Vehicle; }
|
||||
auto dir { 1 - side };
|
||||
while( vehicle != nullptr ) {
|
||||
if( false == Function( vehicle ) ) { return; } // false stops the walk
|
||||
vehicle = vehicle->Neighbour( dir );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TController::ClaimConsistPrimary()
|
||||
{ // this driver is to be the only primary left in the consist
|
||||
ForEachInConsist(
|
||||
pVehicle,
|
||||
[this]( TDynamicObject *Vehicle ) {
|
||||
auto *other { Vehicle->Mechanik };
|
||||
if( other == nullptr
|
||||
|| other == this ) { return true; }
|
||||
if( other->primary() ) {
|
||||
other->ZeroLocalBrake();
|
||||
if( other->mvOccupied->BrakeCtrlPosNo > 0 ) { // only vehicles with a driver's brake valve (otherwise Handle == null)
|
||||
other->mvOccupied->BrakeLevelSet( other->mvOccupied->Handle->GetPos( bh_NP ) );
|
||||
other->BrakeLevelSet( other->mvOccupied->BrakeCtrlPos );
|
||||
}
|
||||
}
|
||||
// always - sync the valve on the already secondary ones too
|
||||
other->primary( false );
|
||||
return true;
|
||||
} );
|
||||
primary( true );
|
||||
TableClear(); // after a change of primary the table has to be built from scratch
|
||||
CheckVehicles(); // otherwise the members' ctOwner stays with the previous primary
|
||||
}
|
||||
|
||||
TController *
|
||||
TController::ActiveEnginePrimary() const {
|
||||
// a primary with the engine already running - then we do not steal primary
|
||||
TController *active { nullptr };
|
||||
ForEachInConsist(
|
||||
pVehicle,
|
||||
[&active]( TDynamicObject *Vehicle ) {
|
||||
auto *driver { Vehicle->Mechanik };
|
||||
if( driver != nullptr
|
||||
&& driver->primary()
|
||||
&& driver->iEngineActive ) {
|
||||
active = driver;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} );
|
||||
return active;
|
||||
}
|
||||
|
||||
bool TController::PutCommand( std::string NewCommand, double NewValue1, double NewValue2, glm::dvec3 const *NewLocation, TStopReason reason )
|
||||
{ // analiza komendy
|
||||
auto const shouldtakeprimary{
|
||||
AIControllFlag
|
||||
&& !primary()
|
||||
&& CommandClaimsPrimary( NewCommand, NewValue1 )
|
||||
&& ActiveEnginePrimary() == nullptr }; // steal only while the engines are down
|
||||
if( shouldtakeprimary ) {
|
||||
ClaimConsistPrimary();
|
||||
if( mvOccupied->CabActive == 0 ) {
|
||||
mvOccupied->CabActivisationAuto();
|
||||
}
|
||||
if( mvOccupied->DirActive == 0 ) {
|
||||
DirectionForward( true );
|
||||
}
|
||||
DirectionChange();
|
||||
}
|
||||
|
||||
if (NewCommand == "CabSignal")
|
||||
{ // SHP wyzwalane jest przez człon z obsadą, ale obsługiwane przez silnikowy
|
||||
// nie jest to najlepiej zrobione, ale bez symulacji obwodów lepiej nie będzie
|
||||
@@ -4459,7 +4644,7 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N
|
||||
TrainParams.StationStart = TrainParams.StationIndex;
|
||||
asNextStop = TrainParams.NextStop();
|
||||
m_lastannouncement = announcement_t::idle;
|
||||
iDrivigFlags |= movePrimary; // skoro dostał rozkład, to jest teraz głównym
|
||||
primary( true );
|
||||
// NewCommand = Global.asCurrentSceneryPath + NewCommand;
|
||||
auto lookup =
|
||||
FileExists(
|
||||
@@ -4882,13 +5067,78 @@ void TController::PhysicsLog()
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
TController::SettleSecondaryAI() {
|
||||
// no force, no brake, vigilance kept quiet; runs every tick so nothing is remembered
|
||||
mvOccupied->BrakeValveActive = false; // the primary drives the brake pipe
|
||||
// without a NN acknowledging alone is not enough (see TSecuritySystem::is_braking)
|
||||
mvOccupied->SecuritySystem.acknowledge_press();
|
||||
mvOccupied->SecuritySystem.acknowledge_release();
|
||||
if( false == ( mvOccupied->Power24vIsAvailable || mvOccupied->Power110vIsAvailable ) ) {
|
||||
mvOccupied->SecuritySystem.set_enabled( false );
|
||||
}
|
||||
ZeroSpeed( true );
|
||||
ZeroLocalBrake();
|
||||
if( mvOccupied->SpringBrake.Activate ) {
|
||||
mvOccupied->SpringBrakeActivate( false );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TController::UpdateSecondary( double const Awarenessrange ) {
|
||||
// does not drive; scans while the consist is shut down (wake-up / taking over on a signal)
|
||||
if( AIControllFlag ) {
|
||||
SettleSecondaryAI();
|
||||
}
|
||||
else {
|
||||
// a human sits in the cab - they handle it themselves
|
||||
mvOccupied->BrakeValveActive = true;
|
||||
}
|
||||
if( pVehicle->ctOwner != nullptr
|
||||
&& pVehicle->ctOwner != this
|
||||
&& pVehicle->ctOwner->iEngineActive ) {
|
||||
return; // the consist runs under an active primary - do not duplicate the scan
|
||||
}
|
||||
// a secondary needs no responsiveness - scans rarely, cheap with many shut down consists
|
||||
auto const scaninterval{ std::max( ReactionTime, EU07_AI_SECONDARYSCANINTERVAL ) };
|
||||
if( LastReactionTime < scaninterval ) { return; }
|
||||
LastReactionTime -= scaninterval;
|
||||
OrientScanToCab();
|
||||
scan_route( Awarenessrange );
|
||||
// no obstacles - a dormant driver only waits for a signal; the primary computes them later
|
||||
ScanForWakeup( Awarenessrange );
|
||||
}
|
||||
|
||||
void
|
||||
TController::ScanForWakeup( double const Range ) {
|
||||
// a permission may wake a dormant AI up (Prepare_engine in the mask)
|
||||
if( AIControllFlag
|
||||
&& ( OrderCurrentGet() & ~( Shunt | Loose_shunt | Obey_train | Bank | Prepare_engine ) ) == 0 ) {
|
||||
check_route_ahead( Range );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TController::Update( double const Timedelta ) {
|
||||
// uruchamiać przynajmniej raz na sekundę
|
||||
if( ( iDrivigFlags & movePrimary ) == 0 ) { return; } // pasywny nic nie robi
|
||||
if( false == simulation::is_ready ) { return; }
|
||||
if( !simulation::is_ready ) { return; }
|
||||
|
||||
update_timers( Timedelta );
|
||||
|
||||
auto const awarenessrange {
|
||||
std::max(
|
||||
750.0,
|
||||
mvOccupied->Vel > EU07_AI_MOVEMENT ?
|
||||
400 + fBrakeDist :
|
||||
30.0 * fDriverDist ) };
|
||||
|
||||
if( !primary() ) {
|
||||
UpdateSecondary( awarenessrange );
|
||||
return;
|
||||
}
|
||||
|
||||
mvOccupied->BrakeValveActive = true; // opens the valve, may have been cut off while secondary
|
||||
|
||||
update_logs( Timedelta );
|
||||
|
||||
auto const reactiontime { std::min( ReactionTime, 2.0 ) };
|
||||
@@ -4913,16 +5163,9 @@ TController::Update( double const Timedelta ) {
|
||||
determine_consist_state();
|
||||
determine_braking_distance();
|
||||
determine_proximity_ranges();
|
||||
// vicinity check
|
||||
auto const awarenessrange {
|
||||
std::max(
|
||||
750.0,
|
||||
mvOccupied->Vel > EU07_AI_MOVEMENT ?
|
||||
400 + fBrakeDist :
|
||||
30.0 * fDriverDist ) }; // 1500m dla stojących pociągów;
|
||||
if( is_active() ) {
|
||||
scan_route( awarenessrange );
|
||||
}
|
||||
|
||||
// we scan while dormant too, to see a permission and wake up
|
||||
scan_route( awarenessrange );
|
||||
scan_obstacles( awarenessrange );
|
||||
// generic actions
|
||||
control_security_system( reactiontime );
|
||||
@@ -5015,8 +5258,12 @@ TController::PrepareDirection() {
|
||||
|
||||
if( iDirection == 0 ) {
|
||||
// jeśli nie ma ustalonego kierunku
|
||||
if( mvOccupied->Vel < EU07_AI_NOMOVEMENT ) { // ustalenie kierunku, gdy stoi
|
||||
// as in DirectionInitial / TakeControl: below 1 km/h counts as standing
|
||||
if( mvOccupied->Vel < EU07_AI_MOVEMENT ) { // determine the direction while standing
|
||||
iDirection = mvOccupied->CabActive; // wg wybranej kabiny
|
||||
if( iDirection == 0 ) {
|
||||
iDirection = mvOccupied->CabOccupied;
|
||||
}
|
||||
/*
|
||||
if( iDirection == 0 ) {
|
||||
// jeśli nie ma ustalonego kierunku
|
||||
@@ -5262,28 +5509,46 @@ std::string TController::StopReasonText() const
|
||||
//- rozpoznają tylko zerową prędkość (jako koniec toru i brak podstaw do dalszego skanowania)
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
bool TController::IsOccupiedByAnotherConsist( TTrack *Track, double const Distance = 0 )
|
||||
{ // najpierw sprawdzamy, czy na danym torze są pojazdy z innego składu
|
||||
if( false == Track->Dynamics.empty() ) {
|
||||
for( auto dynamic : Track->Dynamics ) {
|
||||
if( dynamic->ctOwner != this ) {
|
||||
// jeśli jest jakiś cudzy to tor jest zajęty i skanowanie nie obowiązuje
|
||||
if( Distance == 0 ) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
// based on provided position of scanning vehicle and scan direction, filter out vehicles on irrelevant end
|
||||
auto const scandirection { Distance > 0 ? 1 : -1 }; // positive value means scan towards point2 end of the track
|
||||
auto const obstaclelocation { scandirection > 0 ? Track->Length() - dynamic->RaTranslationGet() : dynamic->RaTranslationGet() };
|
||||
// if detected vehicle is closer to the end of the track in scanned direction than we are, it means it's blocking our way
|
||||
if( obstaclelocation < std::abs( Distance ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool TController::BelongsToConsist( TDynamicObject const *Vehicle ) const
|
||||
{ // ctOwner alone is not enough: cleared after uncoupling until CheckVehicles
|
||||
if( Vehicle == nullptr ) { return false; }
|
||||
if( Vehicle->ctOwner == this ) { return true; }
|
||||
if( Vehicle->Mechanik == this ) { return true; }
|
||||
if( Vehicle->ctOwner != nullptr
|
||||
&& Vehicle->ctOwner == pVehicle->ctOwner ) { return true; }
|
||||
return pVehicle->is_connected( Vehicle ); // with no owner settled, the coupling is all that is left
|
||||
}
|
||||
|
||||
double TController::DistanceToAnotherConsist( TTrack *Track, double const Distance ) const
|
||||
{ // Distance is signed by the scan direction and reaches the far end of the track
|
||||
auto nearest { -1.0 };
|
||||
auto const scandirection { Distance > 0 ? 1 : -1 }; // positive value means scan towards point2 end of the track
|
||||
for( auto *dynamic : Track->Dynamics ) {
|
||||
if( BelongsToConsist( dynamic ) ) { continue; }
|
||||
// how far the foreign vehicle sits from that same far end
|
||||
auto const obstaclelocation { scandirection > 0 ? Track->Length() - dynamic->RaTranslationGet() : dynamic->RaTranslationGet() };
|
||||
// what is left is the gap between us and it; a negative one means it stands behind us
|
||||
auto const distance { std::abs( Distance ) - obstaclelocation };
|
||||
if( distance <= 0.0 ) { continue; }
|
||||
if( nearest < 0.0
|
||||
|| distance < nearest ) {
|
||||
nearest = distance;
|
||||
}
|
||||
}
|
||||
return false; // wolny
|
||||
return nearest;
|
||||
}
|
||||
|
||||
bool TController::IsOccupiedByAnotherConsist( TTrack *Track, double const Distance ) const
|
||||
{ // whether the track carries vehicles of another consist
|
||||
if( Track->Dynamics.empty() ) { return false; }
|
||||
if( Distance == 0.0 ) {
|
||||
// no scan reference given - any foreign vehicle on the track counts
|
||||
for( auto *dynamic : Track->Dynamics ) {
|
||||
if( false == BelongsToConsist( dynamic ) ) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return DistanceToAnotherConsist( Track, Distance ) >= 0.0;
|
||||
};
|
||||
|
||||
basic_event * TController::CheckTrackEventBackward(double fDirection, TTrack *Track, TDynamicObject *Vehicle, int const Eventdirection, end const End)
|
||||
@@ -5614,16 +5879,26 @@ void TController::UpdateDelayFlag() {
|
||||
|
||||
//-----------koniec skanowania semaforow
|
||||
|
||||
void TController::ReleaseTransientControls()
|
||||
{
|
||||
mvOccupied->WarningSignal = 0;
|
||||
mvControlling->SandboxManual( false );
|
||||
fWarningDuration = 0.0;
|
||||
iDrivigFlags &= ~( moveStartHornNow | moveStartHornDone );
|
||||
}
|
||||
|
||||
void TController::TakeControl( bool const Aidriver, bool const Forcevehiclecheck )
|
||||
{ // przejęcie kontroli przez AI albo oddanie
|
||||
if (AIControllFlag == Aidriver && !Forcevehiclecheck)
|
||||
return; // już jest jak ma być
|
||||
ReleaseTransientControls();
|
||||
if (Aidriver) //żeby nie wykonywać dwa razy
|
||||
{ // teraz AI prowadzi
|
||||
AIControllFlag = AIdriver;
|
||||
pVehicle->Controller = AIdriver;
|
||||
control_lights(); // reinicjalizacja swiatel
|
||||
mvOccupied->CabActivisation(true);
|
||||
sync_consist_reversers(); // after taking over from a human the reversers may have drifted apart
|
||||
iDirection = 0; // kierunek jazdy trzeba dopiero zgadnąć
|
||||
TableClear(); // ponowne utworzenie tabelki, bo człowiek mógł pojechać niezgodnie z sygnałami
|
||||
if( action() != TAction::actSleep ) {
|
||||
@@ -5781,13 +6056,14 @@ TController::TrackObstacle() const {
|
||||
|
||||
void TController::MoveTo(TDynamicObject *to)
|
||||
{ // przesunięcie AI do innego pojazdu (przy zmianie kabiny)
|
||||
auto takeprimary{false}; // we take the lead only once the predecessor is gone
|
||||
if( to->Mechanik != nullptr
|
||||
&& to->Mechanik != this ) {
|
||||
// ai controller thunderdome, there can be only one
|
||||
if( to->Mechanik->AIControllFlag ) {
|
||||
if( to->Mechanik->primary() ) {
|
||||
// take over boss duties
|
||||
primary( true );
|
||||
takeprimary = to->Mechanik->primary();
|
||||
if( takeprimary ) {
|
||||
to->Mechanik->primary( false ); // cut the valve before the delete, so it is not left open
|
||||
}
|
||||
SafeDelete( to->Mechanik );
|
||||
}
|
||||
@@ -5803,7 +6079,9 @@ void TController::MoveTo(TDynamicObject *to)
|
||||
pVehicle = to;
|
||||
ControllingSet(); // utworzenie połączenia do sterowanego pojazdu
|
||||
pVehicle->Mechanik = this;
|
||||
|
||||
if( takeprimary ) {
|
||||
primary( true );
|
||||
}
|
||||
};
|
||||
|
||||
void TController::ControllingSet()
|
||||
@@ -5823,14 +6101,32 @@ void TController::ControllingSet()
|
||||
|
||||
std::string TController::TableText( std::size_t const Index ) const
|
||||
{ // pozycja tabelki prędkości
|
||||
if( Index < sSpeedTable.size() ) {
|
||||
return sSpeedTable[ Index ].TableText();
|
||||
}
|
||||
else {
|
||||
if( Index >= sSpeedTable.size() ) {
|
||||
return "";
|
||||
}
|
||||
return sSpeedTable[ Index ].TableText();
|
||||
};
|
||||
|
||||
double TController::TableDistance( std::size_t const Index ) const
|
||||
{ // for slotting the obstacle into the table by distance
|
||||
return
|
||||
Index < sSpeedTable.size() ?
|
||||
sSpeedTable[ Index ].fDist :
|
||||
std::numeric_limits<double>::max();
|
||||
}
|
||||
|
||||
std::string TController::ObstacleText() const
|
||||
{ // a row shaped like TableText, so the panel shows the vehicle at the right distance
|
||||
if( Obstacle.vehicle == nullptr ) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return
|
||||
to_string( Obstacle.distance, 1, 8 )
|
||||
+ " - " + Obstacle.vehicle->asName
|
||||
+ " [vehicle" + ( Obstacle.vehicle->ctOwner == this ? " own-consist]" : "]" );
|
||||
}
|
||||
|
||||
int TController::CrossRoute(TTrack *tr)
|
||||
{ // zwraca numer segmentu dla skrzyżowania (tr)
|
||||
// pożądany numer segmentu jest określany podczas skanowania drogi
|
||||
@@ -6532,6 +6828,27 @@ TController::scan_route( double const Range ) {
|
||||
TableCheck( Range );
|
||||
}
|
||||
|
||||
void
|
||||
TController::OrientScanToCab() {
|
||||
// like the start of CheckVehicles, without primary / lights / ControllingSet
|
||||
iDirection = CheckDirection();
|
||||
if( iDirection == 0 ) { return; }
|
||||
|
||||
auto d{ iDirection >= 0 ? 0 : 1 };
|
||||
auto *p{ pVehicle->FirstFind( d ) };
|
||||
if( p == nullptr ) {
|
||||
p = pVehicle;
|
||||
}
|
||||
pVehicles[ end::front ] = p;
|
||||
auto dir{ 1 - d }; // Neighbour() advances this index to the next member, so it cannot be const
|
||||
fLength = 0.0;
|
||||
while( p != nullptr ) {
|
||||
pVehicles[ end::rear ] = p;
|
||||
fLength += p->MoverParameters->Dim.L;
|
||||
p = p->Neighbour( dir );
|
||||
}
|
||||
}
|
||||
|
||||
// check for potential collisions
|
||||
void
|
||||
TController::scan_obstacles( double const Range ) {
|
||||
@@ -6540,11 +6857,16 @@ TController::scan_obstacles( double const Range ) {
|
||||
// we cast to int to avoid getting confused by microstutters
|
||||
auto *frontvehicle { pVehicles[ ( static_cast<int>( mvOccupied->V ) * iDirection >= 0 ? end::front : end::rear ) ] };
|
||||
|
||||
// no direction set (dormant AI): orient physically by the occupied cab, else both tests below
|
||||
// hold and the scan always runs towards coupler 0 - half the time behind us, taking our own
|
||||
// members for an obstacle and missing the consist standing ahead
|
||||
auto const scandirection { iDirection != 0 ? iDirection : mvOccupied->CabOccupied };
|
||||
|
||||
int routescandirection;
|
||||
// for moving vehicle determine heading from velocity; for standing fall back on the set direction
|
||||
if( std::abs(frontvehicle->MoverParameters->V) > 0.5 ? // ignore potential micro-stutters in oposite direction during "almost stop"
|
||||
frontvehicle->MoverParameters->V > 0.0 :
|
||||
pVehicle->DirectionGet() == frontvehicle->DirectionGet() ? iDirection >= 0 : iDirection <= 0 ) {
|
||||
pVehicle->DirectionGet() == frontvehicle->DirectionGet() ? scandirection >= 0 : scandirection <= 0 ) {
|
||||
// towards coupler 0
|
||||
routescandirection = end::front;
|
||||
}
|
||||
@@ -7118,12 +7440,21 @@ TController::UpdateDisconnect() {
|
||||
|
||||
void
|
||||
TController::handle_engine() {
|
||||
// HACK: activate route scanning if an idling vehicle is activated by a human user
|
||||
if( OrderCurrentGet() == Wait_for_orders
|
||||
&& false == iEngineActive
|
||||
// && ( false == AIControllFlag )
|
||||
&& true == mvOccupied->Power24vIsAvailable ) {
|
||||
OrderNext( Prepare_engine );
|
||||
&& false == iEngineActive ) {
|
||||
// scanning is geometric: the driver sees a signal also with the battery off
|
||||
iDrivigFlags |= moveActive;
|
||||
if( AIControllFlag
|
||||
&& primary()
|
||||
&& mvOccupied->DirActive == 0
|
||||
&& SemNextIndex != -1 ) {
|
||||
// there is a signal ahead and no direction set yet - set it, so the scan runs forwards
|
||||
if( mvOccupied->CabActive == 0 ) {
|
||||
mvOccupied->CabActivisationAuto();
|
||||
}
|
||||
DirectionForward( true );
|
||||
DirectionChange();
|
||||
}
|
||||
}
|
||||
// basic engine preparation
|
||||
if( OrderCurrentGet() == Prepare_engine ) {
|
||||
@@ -7248,8 +7579,8 @@ TController::pick_optimal_speed( double const Range ) {
|
||||
SwitchClearDist = -1;
|
||||
ActualProximityDist = Range; // funkcja Update() może pozostawić wartości bez zmian
|
||||
|
||||
// if we're idling bail out early
|
||||
if( false == is_active() ) {
|
||||
ScanForWakeup( Range );
|
||||
VelDesired = 0.0;
|
||||
VelNext = 0.0;
|
||||
AccDesired = std::min( AccDesired, EU07_AI_NOACCELERATION );
|
||||
@@ -8179,11 +8510,98 @@ void TController::control_main_pipe() {
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
TController::OtherConsistAtSignal() const {
|
||||
// permission goes to the consist in front of the signal, so a foreign vehicle counts only
|
||||
// while it stands closer to us than the signal itself
|
||||
if( SemPermitIndex >= sSpeedTable.size() ) { return false; }
|
||||
|
||||
auto const signaldistance { sSpeedTable[ SemPermitIndex ].fDist };
|
||||
|
||||
for( auto const &point : sSpeedTable ) {
|
||||
// a track starting past the signal cannot hold anything standing in front of it
|
||||
if( point.fDist > signaldistance ) { break; }
|
||||
if( ( point.iFlags & spTrack ) == 0 ) { continue; }
|
||||
if( point.trTrack == nullptr ) { continue; }
|
||||
// only ahead of our front; a consist behind us would make every permission someone else's
|
||||
auto const distancetotrackend { point.fDist + point.trTrack->Length() };
|
||||
if( distancetotrackend <= 0.0 ) { continue; }
|
||||
auto const signeddistance {
|
||||
distancetotrackend * ( ( point.iFlags & spReverse ) != 0 ? 1.0 : -1.0 ) };
|
||||
auto const obstacledistance { DistanceToAnotherConsist( point.trTrack, signeddistance ) };
|
||||
if( obstacledistance < 0.0 ) { continue; } // nothing foreign ahead here
|
||||
if( obstacledistance <= signaldistance ) { return true; }
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
TController::SignalPermissionIsOurs( bool const Signalgivesgo ) const {
|
||||
// the permission belongs to the consist standing closest to the signal
|
||||
if( false == Signalgivesgo ) { return true; }
|
||||
// a shunting permission found already lit was handed to whoever stood here before us; it only
|
||||
// keeps a dormant driver from waking up, and drops as soon as the signal shows stop
|
||||
if( false == iEngineActive
|
||||
&& SemPermitIndex < sSpeedTable.size() // std::size_t( -1 ) never is
|
||||
&& ( sSpeedTable[ SemPermitIndex ].iFlags & spNotOurShuntSignal ) ) {
|
||||
return false;
|
||||
}
|
||||
// the addressee usually cuts the scan short and the signal never makes it into the table;
|
||||
// this closes the case where it did, with the addressee still standing in front of it
|
||||
return false == OtherConsistAtSignal();
|
||||
}
|
||||
|
||||
TController *
|
||||
TController::DepartureDriver() const {
|
||||
// after OrientScanToCab / CheckVehicles the front of the consist is pVehicles[front]
|
||||
if( pVehicles[ end::front ] != nullptr
|
||||
&& pVehicles[ end::front ]->Mechanik != nullptr ) {
|
||||
return pVehicles[ end::front ]->Mechanik;
|
||||
}
|
||||
return const_cast<TController *>( this );
|
||||
}
|
||||
|
||||
void
|
||||
TController::WakeForDeparture( std::string const &Command, double const Value1, double const Value2 ) {
|
||||
// the primary already has the engine running - we do not steal, we just pass the order on
|
||||
if( auto *active { ActiveEnginePrimary() } ) {
|
||||
active->PutCommand( Command, Value1, Value2, nullptr );
|
||||
return;
|
||||
}
|
||||
// engines down - the lead goes to the driver at the front (scanning direction)
|
||||
auto *target { primary() ? this : DepartureDriver() };
|
||||
if( target == nullptr ) {
|
||||
target = this;
|
||||
}
|
||||
if( target != this ) {
|
||||
if( false == target->primary() ) {
|
||||
target->ClaimConsistPrimary();
|
||||
}
|
||||
target->PutCommand( Command, Value1, Value2, nullptr );
|
||||
return;
|
||||
}
|
||||
PutCommand( Command, Value1, Value2, nullptr );
|
||||
}
|
||||
|
||||
void
|
||||
TController::check_route_ahead( double const Range ) {
|
||||
|
||||
auto const comm { TableUpdate( VelDesired, ActualProximityDist, VelNext, AccDesired ) };
|
||||
|
||||
auto const signalpermits { comm == TCommandType::cm_SetVelocity || comm == TCommandType::cm_ShuntVelocity };
|
||||
if( false == SignalPermissionIsOurs( signalpermits ) ) {
|
||||
// the permission was given to another consist, we wait for our own. Remember it on the
|
||||
// entry, so the addressee pulling away does not hand the signal over to us; the flag is
|
||||
// dropped when it shows stop, and only a dormant driver ever carries one
|
||||
if( false == iEngineActive
|
||||
&& SemPermitIndex < sSpeedTable.size()
|
||||
&& ( sSpeedTable[ SemPermitIndex ].iFlags & spShuntSemaphor ) ) {
|
||||
sSpeedTable[ SemPermitIndex ].iFlags |= spNotOurShuntSignal;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (comm) {
|
||||
// ustawienie VelSignal - trochę proteza = do przemyślenia
|
||||
case TCommandType::cm_Ready: { // W4 zezwolił na jazdę
|
||||
@@ -8198,17 +8616,17 @@ TController::check_route_ahead( double const Range ) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TCommandType::cm_SetVelocity: { // od wersji 357 semafor nie budzi wyłączonej lokomotywy
|
||||
if( ( OrderCurrentGet() & ~( Shunt | Loose_shunt | Obey_train | Bank ) ) == 0 ) { // jedzie w dowolnym trybie albo Wait_for_orders
|
||||
case TCommandType::cm_SetVelocity: {
|
||||
if( ( OrderCurrentGet() & ~( Shunt | Loose_shunt | Obey_train | Bank | Prepare_engine ) ) == 0 ) {
|
||||
if( std::fabs( VelSignal ) >= 1.0 ) { // 0.1 nie wysyła się do samochodow, bo potem nie ruszą
|
||||
PutCommand( "SetVelocity", VelSignal, VelNext, nullptr ); // komenda robi dodatkowe operacje
|
||||
WakeForDeparture( "SetVelocity", VelSignal, VelNext );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TCommandType::cm_ShuntVelocity: { // od wersji 357 Tm nie budzi wyłączonej lokomotywy
|
||||
if( ( OrderCurrentGet() & ~( Shunt | Loose_shunt | Obey_train | Bank ) ) == 0 ) { // jedzie w dowolnym trybie albo Wait_for_orders
|
||||
PutCommand( "ShuntVelocity", VelSignal, VelNext, nullptr );
|
||||
case TCommandType::cm_ShuntVelocity: {
|
||||
if( ( OrderCurrentGet() & ~( Shunt | Loose_shunt | Obey_train | Bank | Prepare_engine ) ) == 0 ) {
|
||||
WakeForDeparture( "ShuntVelocity", VelSignal, VelNext );
|
||||
}
|
||||
else if( iCoupler ) { // jeśli jedzie w celu połączenia
|
||||
SetVelocity( VelSignal, VelNext );
|
||||
@@ -8216,8 +8634,7 @@ TController::check_route_ahead( double const Range ) {
|
||||
break;
|
||||
}
|
||||
case TCommandType::cm_Command: { // komenda z komórki
|
||||
if( ( OrderCurrentGet() & ~( Shunt | Loose_shunt | Obey_train | Bank ) ) == 0 ) {
|
||||
// jedzie w dowolnym trybie albo Wait_for_orders
|
||||
if( ( OrderCurrentGet() & ~( Shunt | Loose_shunt | Obey_train | Bank | Prepare_engine ) ) == 0 ) {
|
||||
if( mvOccupied->Vel < 0.1 ) {
|
||||
// dopiero jak stanie
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user