Files
maszyna/Driver.cpp

5303 lines
278 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
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/.
*/
/*
MaSzyna EU07 locomotive simulator
Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others
*/
#include "system.hpp"
#include "classes.hpp"
#pragma hdrstop
#include "Driver.h"
#include <mtable.hpp>
#include "DynObj.h"
#include <math.h>
#include "Globals.h"
#include "Event.h"
#include "Ground.h"
#include "MemCell.h"
#include "World.h"
#include "dir.h"
#define LOGVELOCITY 0
#define LOGORDERS 0
#define LOGSTOPS 1
#define LOGBACKSCAN 0
#define LOGPRESS 0
/*
Moduł obsługujący sterowanie pojazdami (składami pociągów, samochodami).
Ma działać zarówno jako AI oraz przy prowadzeniu przez człowieka. W tym
drugim przypadku jedynie informuje za pomocą napisów o tym, co by zrobił
w tym pierwszym. Obejmuje zarówno maszynistę jak i kierownika pociągu
(dawanie sygnału do odjazdu).
Przeniesiona tutaj została zawartość ai_driver.pas przerobiona na C++.
Również niektóre funkcje dotyczące składów z DynObj.cpp.
Teoria jest wtedy kiedy wszystko wiemy, ale nic nie działa.
Praktyka jest wtedy, kiedy wszystko działa, ale nikt nie wie dlaczego.
Tutaj łączymy teorię z praktyką - tu nic nie działa i nikt nie wie dlaczego…
*/
// zrobione:
// 0. pobieranie komend z dwoma parametrami
// 1. przyspieszanie do zadanej predkosci, ew. hamowanie jesli przekroczona
// 2. hamowanie na zadanym odcinku do zadanej predkosci (ze stabilizacja przyspieszenia)
// 3. wychodzenie z sytuacji awaryjnych: bezpiecznik nadmiarowy, poslizg
// 4. przygotowanie pojazdu do drogi, zmiana kierunku ruchu
// 5. dwa sposoby jazdy - manewrowy i pociagowy
// 6. dwa zestawy psychiki: spokojny i agresywny
// 7. przejscie na zestaw spokojny jesli wystepuje duzo poslizgow lub wybic nadmiarowego.
// 8. lagodne ruszanie (przedluzony czas reakcji na 2 pierwszych nastawnikach)
// 9. unikanie jazdy na oporach rozruchowych
// 10. logowanie fizyki //Ra: nie przeniesione do C++
// 11. kasowanie czuwaka/SHP
// 12. procedury wspomagajace "patrzenie" na odlegle semafory
// 13. ulepszone procedury sterowania
// 14. zglaszanie problemow z dlugim staniem na sygnale S1
// 15. sterowanie EN57
// 16. zmiana kierunku //Ra: z przesiadką po ukrotnieniu
// 17. otwieranie/zamykanie drzwi
// 18. Ra: odczepianie z zahamowaniem i podczepianie
// 19. dla Humandriver: tasma szybkosciomierza - zapis do pliku!
// do zrobienia:
// 1. kierownik pociagu
// 2. madrzejsze unikanie grzania oporow rozruchowych i silnika
// 3. unikanie szarpniec, zerwania pociagu itp
// 4. obsluga innych awarii
// 5. raportowanie problemow, usterek nie do rozwiazania
// 7. samouczacy sie algorytm hamowania
// stałe
const double EasyReactionTime = 0.5; //[s] przebłyski świadomości dla zwykłej jazdy
const double HardReactionTime = 0.2;
const double EasyAcceleration = 0.5; //[m/ss]
const double HardAcceleration = 0.9;
const double PrepareTime = 2.0; //[s] przebłyski świadomości przy odpalaniu
bool WriteLogFlag = false;
AnsiString StopReasonTable[] = {
// przyczyny zatrzymania ruchu AI
"", // stopNone, //nie ma powodu - powinien jechać
"Off", // stopSleep, //nie został odpalony, to nie pojedzie
"Semaphore", // stopSem, //semafor zamknięty
"Time", // stopTime, //czekanie na godzinę odjazdu
"End of track", // stopEnd, //brak dalszej części toru
"Change direction", // stopDir, //trzeba stanąć, by zmienić kierunek jazdy
"Joining", // stopJoin, //zatrzymanie przy (p)odczepianiu
"Block", // stopBlock, //przeszkoda na drodze ruchu
"A command", // stopComm, //otrzymano taką komendę (niewiadomego pochodzenia)
"Out of station", // stopOut, //komenda wyjazdu poza stację (raczej nie powinna zatrzymywać!)
"Radiostop", // stopRadio, //komunikat przekazany radiem (Radiostop)
"External", // stopExt, //przesłany z zewnątrz
"Error", // stopError //z powodu błędu w obliczeniu drogi hamowania
};
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void TSpeedPos::Clear()
{
iFlags = 0; // brak flag to brak reakcji
fVelNext = -1.0; // prędkość bez ograniczeń
fSectionVelocityDist = 0.0; //brak długości
fDist = 0.0;
vPos = vector3(0, 0, 0);
trTrack = NULL; // brak wskaźnika
};
void TSpeedPos::CommandCheck()
{ // sprawdzenie typu komendy w evencie i określenie prędkości
TCommandType command = evEvent->Command();
double value1 = evEvent->ValueGet(1);
double value2 = evEvent->ValueGet(2);
switch (command)
{
case cm_ShuntVelocity:
// prędkość manewrową zapisać, najwyżej AI zignoruje przy analizie tabelki
fVelNext = value1; // powinno być value2, bo druga określa "za"?
iFlags |= spShuntSemaphor;
break;
case cm_SetVelocity:
// w semaforze typu "m" jest ShuntVelocity dla Ms2 i SetVelocity dla S1
// SetVelocity * 0 -> można jechać, ale stanąć przed
// SetVelocity 0 20 -> stanąć przed, potem można jechać 20 (SBL)
// SetVelocity -1 100 -> można jechać, przy następnym ograniczenie (SBL)
// SetVelocity 40 -1 -> PutValues: jechać 40 aż do minięcia (koniec ograniczenia(
fVelNext = value1;
iFlags &= ~(spShuntSemaphor | spPassengerStopPoint | spStopOnSBL);
iFlags |= spSemaphor;// nie manewrowa, nie przystanek, nie zatrzymać na SBL, ale semafor
if (value1 == 0.0) // jeśli pierwsza zerowa
if (value2 != 0.0) // a druga nie
{ // S1 na SBL, można przejechać po zatrzymaniu (tu nie mamy prędkości ani odległości)
fVelNext = value2; // normalnie będzie zezwolenie na jazdę, aby się usunął z tabelki
iFlags |= spStopOnSBL; // flaga, że ma zatrzymać; na pewno nie zezwoli na manewry
}
break;
case cm_SectionVelocity:
// odcinek z ograniczeniem prędkości
fVelNext = value1;
fSectionVelocityDist = value2;
iFlags |= spSectionVel;
break;
case cm_RoadVelocity:
// prędkość drogowa (od tej pory będzie jako domyślna najwyższa)
fVelNext = value1;
iFlags |= spRoadVel;
break;
case cm_PassengerStopPoint:
// nie ma dostępu do rozkładu
// przystanek, najwyżej AI zignoruje przy analizie tabelki
if ((iFlags & spPassengerStopPoint) == 0)
fVelNext = 0.0; // TrainParams->IsStop()?0.0:-1.0; //na razie tak
iFlags |= spPassengerStopPoint; // niestety nie da się w tym miejscu współpracować z rozkładem
break;
case cm_SetProximityVelocity:
// musi zostać gdyż inaczej nie działają manewry
fVelNext = -1;
iFlags |= spProximityVelocity;
// fSectionVelocityDist = value2;
break;
case cm_OutsideStation:
// w trybie manewrowym: skanować od niej wstecz i stanąć po wyjechaniu za sygnalizator i
// zmienić kierunek
// w trybie pociągowym: można przyspieszyć do wskazanej prędkości (po zjechaniu z rozjazdów)
fVelNext = -1;
iFlags |= spOutsideStation; // W5
break;
default:
// inna komenda w evencie skanowanym powoduje zatrzymanie i wysłanie tej komendy
iFlags &= ~(spShuntSemaphor | spPassengerStopPoint |
spStopOnSBL); // nie manewrowa, nie przystanek, nie zatrzymać na SBL
fVelNext = 0.0; // jak nieznana komenda w komórce sygnałowej, to zatrzymujemy
}
};
bool TSpeedPos::Update(vector3 *p, vector3 *dir, double &len)
{ // przeliczenie odległości od punktu (*p), w kierunku (*dir), zaczynając od pojazdu
// dla kolejnych pozycji podawane są współrzędne poprzedniego obiektu w (*p)
vector3 v = vPos - *p; // wektor od poprzedniego obiektu (albo pojazdu) do punktu zmiany
fDist =
v.Length(); // długość wektora to odległość pomiędzy czołem a sygnałem albo początkiem toru
// v.SafeNormalize(); //normalizacja w celu określenia znaku (nie potrzebna?)
if (len == 0.0)
{ // jeżeli liczymy względem pojazdu
double iska = dir ? dir->x * v.x + dir->z * v.z :
fDist; // iloczyn skalarny to rzut na chwilową prostą ruchu
if (iska < 0.0) // iloczyn skalarny jest ujemny, gdy punkt jest z tyłu
{ // jeśli coś jest z tyłu, to dokładna odległość nie ma już większego znaczenia
fDist = -fDist; // potrzebne do badania wyjechania składem poza ograniczenie
if (iFlags & spElapsed) // 32 ustawione, gdy obiekt już został minięty
{ // jeśli minięty (musi być minięty również przez końcówkę składu)
}
else
{
iFlags ^= spElapsed; // 32-minięty - będziemy liczyć odległość względem przeciwnego końca
// toru (nadal może być z przodu i ograniczać)
if ((iFlags & 0x43) == 3) // tylko jeśli (istotny) tor, bo eventy są punktowe
if (trTrack) // może być NULL, jeśli koniec toru (????)
vPos =
(iFlags & spReverse) ?
trTrack->CurrentSegment()->FastGetPoint_0() :
trTrack->CurrentSegment()->FastGetPoint_1(); // drugi koniec istotny
}
}
else if (fDist < 50.0) // przy dużym kącie łuku iloczyn skalarny bardziej zaniży odległość
// niż cięciwa
fDist = iska; // ale przy małych odległościach rzut na chwilową prostą ruchu da
// dokładniejsze wartości
}
if (fDist > 0.0) // nie może być 0.0, a przypadkiem mogło by się trafić i było by źle
if ((iFlags & spElapsed) == 0) // 32 ustawione, gdy obiekt już został minięty
{ // jeśli obiekt nie został minięty, można od niego zliczać narastająco (inaczej może być
// problem z wektorem kierunku)
len = fDist = len + fDist; // zliczanie dlugości narastająco
*p = vPos; // nowy punkt odniesienia
*dir = Normalize(v); // nowy wektor kierunku od poprzedniego obiektu do aktualnego
}
if (iFlags & spTrack) // jeśli tor
{
if (trTrack) // może być NULL, jeśli koniec toru (???)
{
fVelNext = trTrack->VelocityGet(); // aktualizacja prędkości (może być zmieniana
// eventem)
int i;
if ((i = iFlags & 0xF0000000) != 0)
{ // jeśli skrzyżowanie, ograniczyć prędkość przy skręcaniu
if (abs(i) > 0x10000000) //±1 to jazda na wprost, ±2 nieby też, ale z przecięciem
// głównej drogi - chyba że jest równorzędne...
fVelNext = 30.0; // uzależnić prędkość od promienia; albo niech będzie
// ograniczona w skrzyżowaniu (velocity z ujemną wartością)
if ((iFlags & spElapsed) == 0) // jeśli nie wjechał
if (trTrack->iNumDynamics > 0) // a skrzyżowanie zawiera pojazd
{
WriteLog("Tor " + trTrack->NameGet() + " zajety przed pojazdem. Num=" + trTrack->iNumDynamics + "Dist= " + fDist);
fVelNext =
0.0; // to zabronić wjazdu (chyba że ten z przodu też jedzie prosto)
}
}
if (iFlags & spSwitch) // jeśli odcinek zmienny
{
if (bool(trTrack->GetSwitchState() & 1) !=
bool(iFlags & spSwitchStatus)) // czy stan się zmienił?
{ // Ra: zakładam, że są tylko 2 możliwe stany
iFlags ^= spSwitchStatus;
// fVelNext=trTrack->VelocityGet(); //nowa prędkość
if ((iFlags & spElapsed) == 0)
return true; // jeszcze trzeba skanowanie wykonać od tego toru
// problem jest chyba, jeśli zwrotnica się przełoży zaraz po zjechaniu z niej
// na Mydelniczce potrafi skanować na wprost mimo pojechania na bok
}
// poniższe nie dotyczy trybu łączenia?
if ((iFlags & spElapsed) ? false :
trTrack->iNumDynamics >
0) // jeśli jeszcze nie wjechano na tor, a coś na nim jest
{
WriteLog("Rozjazd " + trTrack->NameGet() + " zajety przed pojazdem. Num=" + trTrack->iNumDynamics + "Dist= "+fDist);
//fDist -= 30.0;
fVelNext = 0.0; // to niech stanie w zwiększonej odległości
// else if (fVelNext==0.0) //jeśli została wyzerowana
// fVelNext=trTrack->VelocityGet(); //odczyt prędkości
}
}
}
}
else if (iFlags & spEvent) // jeśli event
{ // 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
}
return false;
};
AnsiString TSpeedPos::TableText()
{ // pozycja tabelki prędkości
if (iFlags & spEnabled)
{ // o ile pozycja istotna
if (iFlags & spTrack) // jeśli tor
return "Flags=#" + IntToHex(iFlags, 8) + ", Dist=" + FloatToStrF(fDist, ffFixed, 7, 1) +
", Vel=" + AnsiString(fVelNext) + ", Track=" + trTrack->NameGet();
else if (iFlags & spEvent) // jeśli event
return "Flags=#" + IntToHex(iFlags, 8) + ", Dist=" + FloatToStrF(fDist, ffFixed, 7, 1) +
", Vel=" + AnsiString(fVelNext) + ", Event=" + evEvent->asName;
}
return "Empty";
}
bool TSpeedPos::Set(TEvent *event, double dist)
{ // zapamiętanie zdarzenia
fDist = dist;
iFlags = spEnabled | spEvent; // event+istotny
evEvent = event;
vPos = event->PositionGet(); // współrzędne eventu albo komórki pamięci (zrzutować na tor?)
CommandCheck(); // sprawdzenie typu komendy w evencie i określenie prędkości
return fVelNext == 0.0; // true gdy zatrzymanie, wtedy nie ma po co skanować dalej
};
void TSpeedPos::Set(TTrack *track, double dist, int flag)
{ // zapamiętanie zmiany prędkości w torze
fDist = dist; // odległość do początku toru
trTrack = track; // TODO: (t) może być NULL i nie odczytamy końca poprzedniego :/
if (trTrack)
{
iFlags = flag | (trTrack->eType == tt_Normal ? 2 : 10); // zapamiętanie kierunku wraz z typem
if (iFlags & spSwitch)
if (trTrack->GetSwitchState() & 1)
iFlags |= spSwitchStatus;
fVelNext = trTrack->VelocityGet();
if (trTrack->iDamageFlag & 128)
fVelNext = 0.0; // jeśli uszkodzony, to też stój
if (iFlags & spEnd)
fVelNext = (trTrack->iCategoryFlag & 1) ?
0.0 :
20.0; // jeśli koniec, to pociąg stój, a samochód zwolnij
vPos = (bool(iFlags & spReverse) != bool(iFlags & spEnd)) ?
trTrack->CurrentSegment()->FastGetPoint_1() :
trTrack->CurrentSegment()->FastGetPoint_0();
}
};
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void TController::TableClear()
{ // wyczyszczenie tablicy
iFirst = iLast = 0;
iTableDirection = 0; // nieznany
for (int i = 0; i < iSpeedTableSize; ++i) // czyszczenie tabeli prędkości
sSpeedTable[i].Clear();
tLast = NULL;
fLastVel = -1;
eSignSkip = NULL; // nic nie pomijamy
};
TEvent *__fastcall TController::CheckTrackEvent(double fDirection, TTrack *Track)
{ // sprawdzanie eventów na podanym torze do podstawowego skanowania
TEvent *e = (fDirection > 0) ? Track->evEvent2 : Track->evEvent1;
if (!e)
return NULL;
if (e->bEnabled)
return NULL;
// jednak wszystkie W4 do tabelki, bo jej czyszczenie na przystanku wprowadza zamieszanie
return e;
}
bool TController::TableAddNew()
{ // zwiększenie użytej tabelki o jeden rekord
iLast = (iLast + 1) % iSpeedTableSize;
// TODO: jeszcze sprawdzić, czy się na iFirst nie nałoży
// TODO: wstawić tu wywołanie odtykacza - teraz jest to w TableTraceRoute()
// TODO: jeśli ostatnia pozycja zajęta, ustawiać dodatkowe flagi - teraz jest to w
// TableTraceRoute()
// TODO: przydało by się też posortować tabelkę wg odległości (ale nie w tym miejscu)
return true; // false gdy się nałoży
};
bool TController::TableNotFound(TEvent *e)
{ // sprawdzenie, czy nie został już dodany do tabelki (np. podwójne W4 robi problemy)
int j = (iLast + 1) % iSpeedTableSize; // j, aby sprawdzić też ostatnią pozycję
for (int i = iFirst; i != j; i = (i + 1) % iSpeedTableSize)
if ((sSpeedTable[i].iFlags & (spEnabled | spEvent)) == (spEnabled |
spEvent)) // o ile używana pozycja
if (sSpeedTable[i].evEvent == e)
{
WriteLog("TableNotFound: Event already in SpeedTable: " + sSpeedTable[i].evEvent->asName);
return false; // już jest, drugi raz dodawać nie ma po co
}
return true; // nie ma, czyli można dodać
};
void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle)
{ // skanowanie trajektorii na odległość (fDistance) od (pVehicle) w kierunku przodu składu i
// uzupełnianie tabelki
if (!iDirection) // kierunek pojazdu z napędem
{ // jeśli kierunek jazdy nie jest okreslony
iTableDirection = 0; // czekamy na ustawienie kierunku
}
TTrack *pTrack; // zaczynamy od ostatniego analizowanego toru
// double fDistChVel=-1; //odległość do toru ze zmianą prędkości
double fTrackLength; // długość aktualnego toru (krótsza dla pierwszego)
double fCurrentDistance; // aktualna przeskanowana długość
TEvent *pEvent;
double fLastDir; // kierunek na ostatnim torze
if (iTableDirection != iDirection)
{ // jeśli zmiana kierunku, zaczynamy od toru ze wskazanym pojazdem
pTrack = pVehicle->RaTrackGet(); // odcinek, na którym stoi
fLastDir = pVehicle->DirectionGet() *
pVehicle->RaDirectionGet(); // ustalenie kierunku skanowania na torze
fCurrentDistance = 0; // na razie nic nie przeskanowano
fTrackLength = pVehicle->RaTranslationGet(); // pozycja na tym torze (odległość od Point1)
if (fLastDir > 0) // jeśli w kierunku Point2 toru
fTrackLength =
pTrack->Length() - fTrackLength; // przeskanowana zostanie odległość do Point2
fLastVel = pTrack->VelocityGet(); // aktualna prędkość
iTableDirection =
iDirection; // ustalenie w jakim kierunku jest wypełniana tabelka względem pojazdu
iFirst = iLast = 0;
tLast = NULL; //żaden nie sprawdzony
}
else
{ // kontynuacja skanowania od ostatnio sprawdzonego toru (w ostatniej pozycji zawsze jest tor)
if (sSpeedTable[iLast].iFlags & spEndOfTable) // zatkanie
{ // jeśli zapełniła się tabelka
if ((iLast + 1) % iSpeedTableSize == iFirst) // jeśli nadal jest zapełniona
{
TablePurger(); // nic się nie da zrobić
return;
}
if ((iLast + 2) % iSpeedTableSize == iFirst) // musi być jeszcze miejsce wolne na
// ewentualny event, bo tor jeszcze nie
// sprawdzony
{
TablePurger();
return; // już lepiej, ale jeszcze nie tym razem
}
sSpeedTable[iLast].iFlags &= 0xBE; // kontynuować próby doskanowania
}
else if (VelNext == 0)
return; // znaleziono semafor lub tor z prędkością zero i nie ma co dalej sprawdzać
//trzeba dalej sprawdzać, gdyż przy stopinfo potrafił zgubić semafor
pTrack = sSpeedTable[iLast].trTrack; // ostatnio sprawdzony tor
if (!pTrack)
return; // koniec toru, to nie ma co sprawdzać (nie ma prawa tak być)
fLastDir = sSpeedTable[iLast].iFlags & spReverse ?
-1.0 :
1.0; // flaga ustawiona, gdy Point2 toru jest bliżej
fCurrentDistance = sSpeedTable[iLast].fDist; // aktualna odległość do jego Point1
fTrackLength =
sSpeedTable[iLast].iFlags & (spElapsed | spEnd) ? 0.0 : pTrack->Length(); // nie doliczać długości gdy:
// 32-minięty początek,
// 64-jazda do końca toru
}
if (fCurrentDistance < fDistance)
{ // jeśli w ogóle jest po co analizować
--iLast; // jak coś się znajdzie, zostanie wpisane w tę pozycję, którą właśnie odczytano
while (fCurrentDistance < fDistance)
{
if (pTrack != tLast) // ostatni zapisany w tabelce nie był jeszcze sprawdzony
{ // jeśli tor nie był jeszcze sprawdzany
if (pTrack)
WriteLog("TableTraceRoute: checking track " + pTrack->NameGet());
if ((pEvent = CheckTrackEvent(fLastDir, pTrack)) !=
NULL) // 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 (TableAddNew())
{
WriteLog("TableTraceRoute: new event found " + pEvent->asName);
if (sSpeedTable[iLast].Set(pEvent,
fCurrentDistance)) // dodanie odczytu sygnału
fDistance = fCurrentDistance; // jeśli sygnał stop, to nie ma
// potrzeby dalej skanować
}
} // event dodajemy najpierw, żeby móc sprawdzić, czy tor został dodany po
// odczytaniu prędkości następnego
if ((pTrack->VelocityGet() == 0.0) // zatrzymanie
|| (pTrack->iAction) // jeśli tor ma własności istotne dla skanowania
|| (pTrack->VelocityGet() != fLastVel)) // następuje zmiana prędkości
{ // odcinek dodajemy do tabelki, gdy jest istotny dla ruchu
if (TableAddNew())
{ // teraz dodatkowo zapamiętanie wybranego segmentu dla skrzyżowania
sSpeedTable[iLast].Set(
pTrack, fCurrentDistance,
fLastDir < 0 ?
5 :
1); // dodanie odcinka do tabelki z flagą kierunku wejścia
if (pTrack->eType == tt_Cross) // na skrzyżowaniach trzeba wybrać segment,
// po którym pojedzie pojazd
{ // dopiero tutaj jest ustalany kierunek segmentu na skrzyżowaniu
sSpeedTable[iLast].iFlags |=
(pTrack->CrossSegment((fLastDir < 0) ? tLast->iPrevDirection :
tLast->iNextDirection,
iRouteWanted) &
15)
<< 28; // ostatnie 4 bity pola flag
sSpeedTable[iLast].iFlags &=
~spReverse; // usunięcie flagi kierunku, bo może być błędna
if (sSpeedTable[iLast].iFlags < 0)
sSpeedTable[iLast].iFlags |=
spReverse; // ustawienie flagi kierunku na podstawie wybranego segmentu
if (int(fLastDir) * sSpeedTable[iLast].iFlags < 0)
fLastDir = -fLastDir;
if (AIControllFlag) // dla AI na razie losujemy kierunek na kolejnym
// skrzyżowaniu
iRouteWanted = 1 + random(3);
}
}
}
else if ((pTrack->fRadius != 0.0) // odległość na łuku lepiej aproksymować cięciwami
|| (tLast ? tLast->fRadius != 0.0 : false)) // koniec łuku też jest istotny
{ // albo dla liczenia odległości przy pomocy cięciw - te usuwać po przejechaniu
if (TableAddNew())
sSpeedTable[iLast].Set(pTrack, fCurrentDistance,
fLastDir < 0 ? 0x85 :
0x81); // dodanie odcinka do tabelki
// 0x85 = spEnabled, spReverse, SpCurve
}
}
fCurrentDistance +=
fTrackLength; // doliczenie kolejnego odcinka do przeskanowanej długości
tLast = pTrack; // odhaczenie, że sprawdzony
// Track->ScannedFlag=true; //do pokazywania przeskanowanych torów
fLastVel = pTrack->VelocityGet(); // prędkość na poprzednio sprawdzonym odcinku
pTrack = pTrack->Neightbour(
(pTrack->eType == tt_Cross) ? (sSpeedTable[iLast].iFlags >> 28) : int(fLastDir),
fLastDir); // może być NULL
/*
if (fLastDir>0)
{//jeśli szukanie od Point1 w kierunku Point2
pTrack=pTrack->CurrentNext(); //może być NULL
if (pTrack) //jeśli dalej brakuje toru, to zostajemy na tym samym, z tą samą
orientacją
if (tLast->iNextDirection)
fLastDir=-fLastDir; //można by zamiętać i zmienić tylko jeśli jest pTrack
}
else //if (fDirection<0)
{//jeśli szukanie od Point2 w kierunku Point1
pTrack=pTrack->CurrentPrev(); //może być NULL
if (pTrack) //jeśli dalej brakuje toru, to zostajemy na tym samym, z tą samą
orientacją
if (!tLast->iPrevDirection)
fLastDir=-fLastDir;
}
*/
if (pTrack)
{ // jeśli kolejny istnieje
if (tLast)
if (pTrack->VelocityGet() < 0 ? tLast->VelocityGet() > 0 :
pTrack->VelocityGet() > tLast->VelocityGet())
{ // jeśli kolejny ma większą prędkość niż poprzedni, to zapamiętać poprzedni
// (do czasu wyjechania)
if ((sSpeedTable[iLast].iFlags & 3) == 3 ?
(sSpeedTable[iLast].trTrack != tLast) :
true) // jeśli nie był dodany do tabelki
if (TableAddNew())
sSpeedTable[iLast].Set(
tLast, fCurrentDistance,
(fLastDir > 0 ? pTrack->iPrevDirection :
pTrack->iNextDirection) ?
1 :
5); // zapisanie toru z ograniczeniem prędkości
}
if (((iLast + 3) % iSpeedTableSize == iFirst) ?
true :
((iLast + 2) % iSpeedTableSize == iFirst)) // czy tabelka się nie zatka?
{ // jest ryzyko nieznalezienia ograniczenia - ograniczyć prędkość do pozwalającej
// na zatrzymanie na końcu przeskanowanej drogi
TablePurger(); // usunąć pilnie zbędne pozycje
if (((iLast + 3) % iSpeedTableSize == iFirst) ?
true :
((iLast + 2) % iSpeedTableSize == iFirst)) // czy tabelka się nie zatka?
{ // jeśli odtykacz nie pomógł (TODO: zwiększyć rozmiar tabelki)
if (TableAddNew())
sSpeedTable[iLast].Set(
pTrack, fCurrentDistance,
fLastDir < 0 ?
0x10045 :
0x10041); // zapisanie toru jako końcowego (ogranicza prędkosć)
// zapisać w logu, że należy poprawić scenerię?
return; // nie skanujemy dalej, bo nie ma miejsca
}
}
fTrackLength = pTrack->Length(); // zwiększenie skanowanej odległości tylko jeśli
// istnieje dalszy tor
}
else
{ // definitywny koniec skanowania, chyba że dalej puszczamy samochód po gruncie...
if (TableAddNew()) // kolejny, bo się cofnęliśmy o 1
sSpeedTable[iLast].Set(
tLast, fCurrentDistance,
fLastDir < 0 ? 0x45 : 0x41); // zapisanie ostatniego sprawdzonego toru
return; // to ostatnia pozycja, bo NULL nic nie da, a może się podpiąć obrotnica,
// czy jakieś transportery
}
}
if (TableAddNew())
sSpeedTable[iLast].Set(pTrack, fCurrentDistance,
fLastDir < 0 ? 4 : 0); // zapisanie ostatniego sprawdzonego toru
}
};
void TController::TableCheck(double fDistance)
{ // przeliczenie odległości w tabelce, ewentualnie doskanowanie (bez analizy prędkości itp.)
if (iTableDirection != iDirection)
TableTraceRoute(fDistance,
pVehicles[1]); // jak zmiana kierunku, to skanujemy od końca składu
else if (iTableDirection)
{ // trzeba sprawdzić, czy coś się zmieniło
vector3 dir =
pVehicles[0]->VectorFront() * pVehicles[0]->DirectionGet(); // wektor kierunku jazdy
vector3 pos = pVehicles[0]->HeadPosition(); // zaczynamy od pozycji pojazdu
// double lastspeed=-1; //prędkość na torze do usunięcia
double len = 0.0; // odległość będziemy zliczać narastająco
for (int i = iFirst; i != iLast; i = (i + 1) % iSpeedTableSize)
{ // aktualizacja rekordów z wyjątkiem ostatniego
if (sSpeedTable[i].iFlags & spEnabled) // jeśli pozycja istotna
{
if (sSpeedTable[i].Update(&pos, &dir, len))
{
WriteLog("TableCheck: Switch change. Delete next entries. (" +
sSpeedTable[i].trTrack->NameGet() + ")");
int k = (iLast + 1) % iSpeedTableSize; // skanujemy razem z ostatnią pozycją
for (int j = (i+1) % iSpeedTableSize; j != k; j = (j + 1) % iSpeedTableSize)
{ // kasowanie wszystkich rekordów za zmienioną zwrotnicą
if (sSpeedTable[j].iFlags & spTrack)
WriteLog("TableCheck: Delete from table: " + sSpeedTable[j].trTrack->NameGet());
else if (sSpeedTable[j].iFlags & spEvent)
WriteLog("TableCheck: Delete from table: " + sSpeedTable[j].evEvent->asName);
sSpeedTable[j].iFlags = 0;
}
WriteLog("TableCheck: Delete entries OK.");
if (sSpeedTable[i].iFlags & spTrack)
WriteLog("TableCheck: New last element: " + sSpeedTable[i].trTrack->NameGet());
else if (sSpeedTable[i].iFlags & spEvent)
WriteLog("TableCheck: New last element: " + sSpeedTable[i].evEvent->asName);
iLast = i; // pokazujemy gdzie jest ostatni kawałek
break; // nie kontynuujemy pętli, trzeba doskanować ciąg dalszy
}
if (sSpeedTable[i].iFlags & spTrack) // jeśli odcinek
{
if (sSpeedTable[i].fDist < -fLength) // a skład wyjechał całą długością poza
{ // degradacja pozycji
// WriteLog( "TableCheck: Track is behind. Delete from table: " + sSpeedTable[i].trTrack->NameGet());
sSpeedTable[i].iFlags &= ~spEnabled; // nie liczy się
}
else if ((sSpeedTable[i].iFlags & 0xF0000028) ==
spElapsed) // jest z tyłu (najechany) i nie jest zwrotnicą ani skrzyżowaniem
if (sSpeedTable[i].fVelNext < 0) // a nie ma ograniczenia prędkości
{
sSpeedTable[i].iFlags =
0; // to nie ma go po co trzymać (odtykacz usunie ze środka)
// WriteLog("TableCheck: Track without speed. Delete from table: " + sSpeedTable[i].trTrack->NameGet());
}
}
else if (sSpeedTable[i].iFlags & spEvent) // jeśli event
{
if (sSpeedTable[i].fDist < (sSpeedTable[i].evEvent->Type == tp_PutValues ?
-fLength :
0)) // jeśli jest z tyłu
if ((mvOccupied->CategoryFlag & 1) ? false :
sSpeedTable[i].fDist < -fLength)
{ // pociąg staje zawsze, a samochód tylko jeśli nie przejedzie całą
// długością (może być zaskoczony zmianą)
// WriteLog("TableCheck: Event is behind. Delete from table: " + sSpeedTable[i].evEvent->asName);
sSpeedTable[i].iFlags &= ~1; // degradacja pozycji dla samochodu;
// semafory usuwane tylko przy sprawdzaniu,
// bo wysyłają komendy
}
}
// if (sSpeedTable[i].fDist<-20.0*fLength) //jeśli to coś jest 20 razy dalej niż
// długość składu
//{sSpeedTable[i].iFlags&=~1; //to jest to jakby błąd w scenerii
// //WriteLog("Error: too distant object in scan table");
//}
// if (sSpeedTable[i].fDist>20.0*fLength) //jeśli to coś jest 20 razy dalej niż
// długość składu
//{sSpeedTable[i].iFlags&=~1; //to jest to jakby błąd w scenerii
// //WriteLog("Error: too distant object in scan table");
//}
}
if (i == iFirst) // jeśli jest pierwszą pozycją tabeli
{ // pozbycie się początkowej pozycji
if ((sSpeedTable[i].iFlags & 1) ==
0) // jeśli pozycja istotna (po Update() może się zmienić)
// if (iFirst!=iLast) //ostatnia musi zostać - to załatwia for()
iFirst = (iFirst + 1) %
iSpeedTableSize; // kolejne sprawdzanie będzie już od następnej pozycji
}
}
sSpeedTable[iLast].Update(&pos, &dir, len); // aktualizacja ostatniego
if (sSpeedTable[iLast].fDist < fDistance)
TableTraceRoute(fDistance, pVehicles[1]); // doskanowanie dalszego odcinka
}
};
TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fNext, double &fAcc)
{ // ustalenie parametrów, zwraca typ komendy, jeśli sygnał podaje prędkość do jazdy
// fVelDes - prędkość zadana
// fDist - dystans w jakim należy rozważyć ruch
// fNext - prędkość na końcu tego dystansu
// fAcc - zalecane przyspieszenie w chwili obecnej - kryterium wyboru dystansu
double a; // przyspieszenie
double v; // prędkość
double d; // droga
TCommandType go = cm_Unknown;
eSignNext = NULL;
int i, k = iLast - iFirst + 1;
if (k < 0)
k += iSpeedTableSize; // ilość pozycji do przeanalizowania
iDrivigFlags &= ~(moveTrackEnd | moveSwitchFound | moveSemaphorFound |
moveSpeedLimitFound); // te flagi są ustawiane tutaj, w razie potrzeby
for (i = iFirst; k > 0; --k, i = (i + 1) % iSpeedTableSize)
{ // sprawdzenie rekordów od (iFirst) do (iLast), o ile są istotne
if (sSpeedTable[i].iFlags & spEnabled) // badanie istotności
{ // o ile dana pozycja tabelki jest istotna
if (sSpeedTable[i].iFlags & spPassengerStopPoint)
{ // jeśli przystanek, trzeba obsłużyć wg rozkładu
if (sSpeedTable[i].evEvent->CommandGet() != asNextStop)
{ // jeśli nazwa nie jest zgodna
if (sSpeedTable[i].fDist < -fLength) // jeśli został przejechany
sSpeedTable[i].iFlags =
0; // to można usunąć (nie mogą być usuwane w skanowaniu)
continue; // ignorowanie jakby nie było tej pozycji
}
else if (iDrivigFlags &
moveStopPoint) // jeśli pomijanie W4, to nie sprawdza czasu odjazdu
{ // tylko gdy nazwa zatrzymania się zgadza
if (!TrainParams->IsStop())
{ // jeśli nie ma tu postoju
sSpeedTable[i].fVelNext = -1; // maksymalna prędkość w tym miejscu
if (sSpeedTable[i].fDist <
200.0) // przy 160km/h jedzie 44m/s, to da dokładność rzędu 5 sekund
{ // zaliczamy posterunek w pewnej odległości przed (choć W4 nie zasłania
// już semafora)
#if LOGSTOPS
WriteLog(pVehicle->asName + " as " + TrainParams->TrainName + ": at " +
AnsiString(GlobalTime->hh) + ":" + AnsiString(GlobalTime->mm) +
" skipped " + asNextStop); // informacja
#endif
fLastStopExpDist = mvOccupied->DistCounter + 0.250 +
0.001 * fLength; // przy jakim dystansie (stanie
// licznika) ma przesunąć na
// następny postój
TrainParams->UpdateMTable(
GlobalTime->hh, GlobalTime->mm,
asNextStop.SubString(20, asNextStop.Length()));
TrainParams->StationIndexInc(); // przejście do następnej
asNextStop =
TrainParams->NextStop(); // pobranie kolejnego miejsca zatrzymania
// TableClear(); //aby od nowa sprawdziło W4 z inną nazwą już - to nie
// jest dobry pomysł
sSpeedTable[i].iFlags = 0; // nie liczy się już
sSpeedTable[i].fVelNext = -1; // jechać
continue; // nie analizować prędkości
}
} // koniec obsługi przelotu na W4
else
{ // zatrzymanie na W4
if (!eSignNext)
eSignNext = sSpeedTable[i].evEvent;
if (mvOccupied->Vel > 0.3) // jeśli jedzie (nie trzeba czekać, aż się
// drgania wytłumią - drzwi zamykane od 1.0)
sSpeedTable[i].fVelNext = 0; // to będzie zatrzymanie
// else if
// ((iDrivigFlags&moveStopCloser)?sSpeedTable[i].fDist<=fMaxProximityDist*(AIControllFlag?1.0:10.0):true)
else if ((iDrivigFlags & moveStopCloser) ?
sSpeedTable[i].fDist + fLength <=
Max0R(sSpeedTable[i].evEvent->ValueGet(2),
fMaxProximityDist + fLength) :
true)
// Ra 2F1I: odległość plus długość pociągu musi być mniejsza od długości
// peronu, chyba że pociąg jest dłuższy, to wtedy minimalna
// jeśli długość peronu ((sSpeedTable[i].evEvent->ValueGet(2)) nie podana,
// przyjąć odległość fMinProximityDist
{ // jeśli się zatrzymał przy W4, albo stał w momencie zobaczenia W4
if (!AIControllFlag) // AI tylko sobie otwiera drzwi
iDrivigFlags &= ~moveStopCloser; // w razie przełączenia na AI ma
// nie podciągać do W4, gdy
// użytkownik zatrzymał za daleko
if ((iDrivigFlags & moveDoorOpened) == 0)
{ // drzwi otwierać jednorazowo
iDrivigFlags |= moveDoorOpened; // nie wykonywać drugi raz
if (mvOccupied->DoorOpenCtrl == 1) //(mvOccupied->TrainType==dt_EZT)
{ // otwieranie drzwi w EZT
if (AIControllFlag) // tylko AI otwiera drzwi EZT, użytkownik
// musi samodzielnie
if (!mvOccupied->DoorLeftOpened &&
!mvOccupied->DoorRightOpened)
{ // otwieranie drzwi
int p2 =
int(floor(sSpeedTable[i].evEvent->ValueGet(2))) %
10; // p7=platform side (1:left, 2:right, 3:both)
int lewe = (iDirection > 0) ? 1 : 2; // jeśli jedzie do
// tyłu, to drzwi
// otwiera
// odwrotnie
int prawe = (iDirection > 0) ? 2 : 1;
if (p2 & lewe)
mvOccupied->DoorLeft(true);
if (p2 & prawe)
mvOccupied->DoorRight(true);
// if (p2&3) //żeby jeszcze poczekał chwilę, zanim
// zamknie
// WaitingSet(10); //10 sekund (wziąć z rozkładu????)
}
}
else
{ // otwieranie drzwi w składach wagonowych - docelowo wysyłać
// komendę zezwolenia na otwarcie drzwi
int p7, lewe,
prawe; // p7=platform side (1:left, 2:right, 3:both)
p7 = int(floor(sSpeedTable[i].evEvent->ValueGet(2))) %
10; // tu będzie jeszcze długość peronu zaokrąglona do 10m
// (20m bezpieczniej, bo nie modyfikuje bitu 1)
TDynamicObject *p = pVehicles[0]; // pojazd na czole składu
while (p)
{ // otwieranie drzwi w pojazdach - flaga zezwolenia była by
// lepsza
lewe = (p->DirectionGet() > 0) ? 1 : 2; // jeśli jedzie do
// tyłu, to drzwi
// otwiera odwrotnie
prawe = 3 - lewe;
p->MoverParameters->BatterySwitch(true); // wagony muszą
// mieć baterię
// załączoną do
// otwarcia
// drzwi...
if (p7 & lewe)
p->MoverParameters->DoorLeft(true);
if (p7 & prawe)
p->MoverParameters->DoorRight(true);
p = p->Next(); // pojazd podłączony z tyłu (patrząc od
// czoła)
}
// if (p7&3) //żeby jeszcze poczekał chwilę, zanim zamknie
// WaitingSet(10); //10 sekund (wziąć z rozkładu????)
}
if (fStopTime >
-5) // na końcu rozkładu się ustawia 60s i tu by było skrócenie
WaitingSet(10); // 10 sekund (wziąć z rozkładu????) - czekanie
// niezależne od sposobu obsługi drzwi, bo
// opóźnia również kierownika
}
if (TrainParams->UpdateMTable(
GlobalTime->hh, GlobalTime->mm,
asNextStop.SubString(20, asNextStop.Length())))
{ // to się wykona tylko raz po zatrzymaniu na W4
if (TrainParams->CheckTrainLatency() < 0.0)
iDrivigFlags |= moveLate; // odnotowano spóźnienie
else
iDrivigFlags &= ~moveLate; // przyjazd o czasie
if (TrainParams->DirectionChange()) // jeśli "@" w rozkładzie, to
// wykonanie dalszych komend
{ // wykonanie kolejnej komendy, nie dotyczy ostatniej stacji
if (iDrivigFlags & movePushPull) // SN61 ma się też nie ruszać,
// chyba że ma wagony
{
iDrivigFlags |= moveStopHere; // EZT ma stać przy peronie
if (OrderNextGet() != Change_direction)
{
OrderPush(Change_direction); // zmiana kierunku
OrderPush(TrainParams->StationIndex <
TrainParams->StationCount ?
Obey_train :
Shunt); // to dalej wg rozkładu
}
}
else // a dla lokomotyw...
iDrivigFlags &=
~(moveStopPoint | moveStopHere); // pozwolenie na
// przejechanie za W4
// przed czasem i nie
// ma stać
JumpToNextOrder(); // przejście do kolejnego rozkazu (zmiana
// kierunku, odczepianie)
iDrivigFlags &= ~moveStopCloser; // ma nie podjeżdżać pod W4 po
// przeciwnej stronie
sSpeedTable[i].iFlags = 0; // ten W4 nie liczy się już zupełnie
// (nie wyśle SetVelocity)
sSpeedTable[i].fVelNext = -1; // jechać
continue; // nie analizować prędkości
}
}
if (OrderCurrentGet() == Shunt)
{
OrderNext(Obey_train); // uruchomić jazdę pociągową
CheckVehicles(); // zmienić światła
}
if (TrainParams->StationIndex < TrainParams->StationCount)
{ // jeśli są dalsze stacje, czekamy do godziny odjazdu
if (TrainParams->IsTimeToGo(GlobalTime->hh, GlobalTime->mm))
{ // z dalszą akcją czekamy do godziny odjazdu
// if (TrainParams->CheckTrainLatency()<0.0) //jak się ma odjazd
// do czasu odjazdu?
// iDrivigFlags|=moveLate1; //oflagować, gdy odjazd ze
// spóźnieniem, będzie jechał forsowniej
fLastStopExpDist =
mvOccupied->DistCounter + 0.050 +
0.001 * fLength; // przy jakim dystansie (stanie licznika)
// ma przesunąć na następny postój
// Controlled-> //zapisać odległość do przejechania
TrainParams->StationIndexInc(); // przejście do następnej
asNextStop =
TrainParams
->NextStop(); // pobranie kolejnego miejsca zatrzymania
// TableClear(); //aby od nowa sprawdziło W4 z inną nazwą już - to nie jest dobry pomysł
#if LOGSTOPS
WriteLog(pVehicle->asName + " as " + TrainParams->TrainName +
": at " + AnsiString(GlobalTime->hh) + ":" +
AnsiString(GlobalTime->mm) + " next " +
asNextStop); // informacja
#endif
if (int(floor(sSpeedTable[i].evEvent->ValueGet(1))) & 1)
iDrivigFlags |= moveStopHere; // nie podjeżdżać do semafora,
// jeśli droga nie jest wolna
else
iDrivigFlags &= ~moveStopHere; //po czasie jedź dalej
iDrivigFlags |= moveStopCloser; // do następnego W4 podjechać
// blisko (z dociąganiem)
iDrivigFlags &= ~moveStartHorn; // bez trąbienia przed odjazdem
sSpeedTable[i].iFlags =
0; // nie liczy się już zupełnie (nie wyśle SetVelocity)
sSpeedTable[i].fVelNext = -1; // można jechać za W4
if (go == cm_Unknown) // jeśli nie było komendy wcześniej
go = cm_Ready; // gotów do odjazdu z W4 (semafor może
// zatrzymać)
if (tsGuardSignal) // jeśli mamy głos kierownika, to odegrać
iDrivigFlags |= moveGuardSignal;
continue; // nie analizować prędkości
} // koniec startu z zatrzymania
} // koniec obsługi początkowych stacji
else
{ // jeśli dojechaliśmy do końca rozkładu
#if LOGSTOPS
WriteLog(pVehicle->asName + " as " + TrainParams->TrainName +
": at " + AnsiString(GlobalTime->hh) + ":" +
AnsiString(GlobalTime->mm) +
" end of route."); // informacja
#endif
asNextStop = TrainParams->NextStop(); // informacja o końcu trasy
TrainParams->NewName("none"); // czyszczenie nieaktualnego rozkładu
// TableClear(); //aby od nowa sprawdziło W4 z inną nazwą już - to
// nie jest dobry pomysł
iDrivigFlags &=
~(moveStopCloser |
moveStopPoint); // ma nie podjeżdżać pod W4 i ma je pomijać
sSpeedTable[i].iFlags =
0; // W4 nie liczy się już (nie wyśle SetVelocity)
sSpeedTable[i].fVelNext = -1; // można jechać za W4
fLastStopExpDist = -1.0f; // nie ma rozkładu, nie ma usuwania stacji
WaitingSet(60); // tak ze 2 minuty, aż wszyscy wysiądą
JumpToNextOrder(); // wykonanie kolejnego rozkazu (Change_direction
// albo Shunt)
iDrivigFlags |= moveStopHere | moveStartHorn; // ma się nie ruszać
// aż do momentu
// podania sygnału
continue; // nie analizować prędkości
} // koniec obsługi ostatniej stacji
} // if (MoverParameters->Vel==0.0)
} // koniec obsługi zatrzymania na W4
} // koniec warunku pomijania W4 podczas zmiany czoła
else
{ // skoro pomijanie, to jechać i ignorować W4
sSpeedTable[i].iFlags = 0; // W4 nie liczy się już (nie zatrzymuje jazdy)
sSpeedTable[i].fVelNext = -1;
continue; // nie analizować prędkości
}
} // koniec obsługi W4
v = sSpeedTable[i].fVelNext; // odczyt prędkości do zmiennej pomocniczej
if (sSpeedTable[i].iFlags &
spSwitch) // zwrotnice są usuwane z tabelki dopiero po zjechaniu z nich
iDrivigFlags |=
moveSwitchFound; // rozjazd z przodu/pod ogranicza np. sens skanowania wstecz
else if (sSpeedTable[i].iFlags & spEvent) // W4 może się deaktywować
{ // jeżeli event, może być potrzeba wysłania komendy, aby ruszył
//sprawdzanie eventów pasywnych miniętych
if (sSpeedTable[i].iFlags & spOutsideStation)
{ // jeśli W5, to reakcja zależna od trybu jazdy
if (OrderCurrentGet() & Obey_train)
{ // w trybie pociągowym: można przyspieszyć do wskazanej prędkości (po
// zjechaniu z rozjazdów)
v = -1.0; // ignorować?
//TODO trzeba zmienić przypisywanie VelSignal na VelSignalLast
if (sSpeedTable[i].fDist < 0.0) // jeśli wskaźnik został minięty
{
VelSignalLast = v; //ustawienie prędkości na -1
// iStationStart=TrainParams->StationIndex; //zaktualizować
// wyświetlanie rozkładu
}
else if (!(iDrivigFlags & moveSwitchFound)) // jeśli rozjazdy już minięte
VelSignalLast = v; //!!! to też koniec ograniczenia
}
else
{ // w trybie manewrowym: skanować od niego wstecz, stanąć po wyjechaniu za
// sygnalizator i zmienić kierunek
v = 0.0; // zmiana kierunku może być podanym sygnałem, ale wypadało by
// zmienić światło wcześniej
if (!(iDrivigFlags & moveSwitchFound)) // jeśli nie ma rozjazdu
iDrivigFlags |= moveTrackEnd; // to dalsza jazda trwale ograniczona (W5,
// koniec toru)
}
}
else if (sSpeedTable[i].iFlags & spStopOnSBL)
{ // jeśli S1 na SBL
if (mvOccupied->Vel < 2.0) // stanąć nie musi, ale zwolnić przynajmniej
if (sSpeedTable[i].fDist < fMaxProximityDist) // jest w maksymalnym zasięgu
{
eSignSkip = sSpeedTable[i]
.evEvent; // to można go pominąć (wziąć drugą prędkosć)
iDrivigFlags |= moveVisibility; // jazda na widoczność - skanować
// możliwość kolizji i nie podjeżdżać
// zbyt blisko
// usunąć flagę po podjechaniu blisko semafora zezwalającego na jazdę
// ostrożnie interpretować sygnały - semafor może zezwalać na jazdę
// pociągu z przodu!
}
if (eSignSkip != sSpeedTable[i].evEvent) // jeśli ten SBL nie jest do pominięcia
// TODO sprawdzić do której zmiennej jest przypisywane v i zmienić to tutaj
v = sSpeedTable[i].evEvent->ValueGet(1); // to ma 0 odczytywać
}
else if (sSpeedTable[i].iFlags & spSemaphor)
{ // to semaphor
if (sSpeedTable[i].fDist < 0)
VelSignalLast = sSpeedTable[i].fVelNext; //minięty daje prędkość obowiązującą
else
iDrivigFlags |= moveSemaphorFound; //jeśli z przodu to dajemy falgę, że jest
}
else if (sSpeedTable[i].iFlags & spRoadVel)
{ // to W6
if (sSpeedTable[i].fDist < 0)
VelRoad = sSpeedTable[i].fVelNext;
}
else if (sSpeedTable[i].iFlags & spSectionVel)
{ // to W27
if (sSpeedTable[i].fDist < 0) // teraz trzeba sprawdzić inne warunki
{
if (sSpeedTable[i].fSectionVelocityDist == 0.0)
{
WriteLog("TableUpdate: Event is behind. SVD = 0: " + sSpeedTable[i].evEvent->asName);
sSpeedTable[i].iFlags = 0; // jeśli punktowy to kasujemy i nie dajemy ograniczenia na stałe
}
else if (sSpeedTable[i].fSectionVelocityDist < 0.0)
{ // ograniczenie obowiązujące do następnego
if (sSpeedTable[i].fVelNext == Global::Min0RSpeed(sSpeedTable[i].fVelNext, VelLimitLast) &&
sSpeedTable[i].fVelNext != VelLimitLast)
{ // jeśli ograniczenie jest mniejsze niż obecne to obowiązuje od zaraz
VelLimitLast = sSpeedTable[i].fVelNext;
}
else if (sSpeedTable[i].fDist < -fLength)
{ // jeśli większe to musi wyjechać za poprzednie
VelLimitLast = sSpeedTable[i].fVelNext;
WriteLog("TableUpdate: Event is behind. SVD < 0: " + sSpeedTable[i].evEvent->asName);
sSpeedTable[i].iFlags = 0; // wyjechaliśmy poza poprzednie, można skasować
}
}
else
{ // jeśli większe to ograniczenie ma swoją długość
if (sSpeedTable[i].fVelNext == Global::Min0RSpeed(sSpeedTable[i].fVelNext, VelLimitLast) &&
sSpeedTable[i].fVelNext != VelLimitLast)
{ // jeśli ograniczenie jest mniejsze niż obecne to obowiązuje od zaraz
VelLimitLast = sSpeedTable[i].fVelNext;
}
else if (sSpeedTable[i].fDist < -fLength && sSpeedTable[i].fVelNext != VelLimitLast)
{ // jeśli większe to musi wyjechać za poprzednie
VelLimitLast = sSpeedTable[i].fVelNext;
}
else if (sSpeedTable[i].fDist < -fLength - sSpeedTable[i].fSectionVelocityDist)
{ //
VelLimitLast = -1.0;
WriteLog("TableUpdate: Event is behind. SVD > 0: " + sSpeedTable[i].evEvent->asName);
sSpeedTable[i].iFlags = 0; // wyjechaliśmy poza poprzednie, można skasować
}
}
}
}
//sprawdzenie eventów pasywnych przed nami
if ((mvOccupied->CategoryFlag & 1) ?
sSpeedTable[i].fDist > pVehicles[0]->fTrackBlock - 20.0 :
false) // jak sygnał jest dalej niż zawalidroga
v = 0.0; // to może być podany dla tamtego: jechać tak, jakby tam stop był
else
{ // zawalidrogi nie ma (albo pojazd jest samochodem), sprawdzić sygnał
if (sSpeedTable[i].iFlags & spShuntSemaphor) // jeśli Tm - w zasadzie to sprawdzić
// komendę!
{ // jeśli podana prędkość manewrowa
if ((OrderCurrentGet() & Obey_train) ? v == 0.0 : false)
{ // jeśli tryb pociągowy a tarcze ma ShuntVelocity 0 0
v = -1; // ignorować, chyba że prędkość stanie się niezerowa
if (sSpeedTable[i].iFlags & spElapsed) // a jak przejechana
sSpeedTable[i].iFlags = 0; // to można usunąć, bo podstawowy automat
// usuwa tylko niezerowe
}
else if (go == cm_Unknown) // jeśli jeszcze nie ma komendy
if (v != 0.0) // komenda jest tylko gdy ma jechać, bo stoi na podstawie
// tabelki
{ // jeśli nie było komendy wcześniej - pierwsza się liczy - ustawianie
// VelSignal
go = cm_ShuntVelocity; // w trybie pociągowym tylko jeśli włącza
// tryb manewrowy (v!=0.0)
// Ra 2014-06: (VelSignal) nie może być tu ustawiane, bo Tm może być
// daleko
// VelSignal=v; //nie do końca tak, to jest druga prędkość
if (VelSignal == 0.0)
VelSignal = v; // aby stojący ruszył
if (sSpeedTable[i].fDist < 0.0) // jeśli przejechany
{
VelSignal = v; //!!! ustawienie, gdy przejechany jest lepsze niż
// wcale, ale to jeszcze nie to
sSpeedTable[i].iFlags =
0; // to można usunąć (nie mogą być usuwane w skanowaniu)
}
}
}
else if (!(sSpeedTable[i].iFlags & spSectionVel)) //jeśli jakiś event pasywny ale nie ograniczenie
if (go == cm_Unknown) // jeśli nie było komendy wcześniej - pierwsza się liczy
// - ustawianie VelSignal
if (v < 0.0 ? true : v >= 1.0) // bo wartość 0.1 służy do hamowania tylko
{
go = cm_SetVelocity; // może odjechać
// Ra 2014-06: (VelSignal) nie może być tu ustawiane, bo semafor może
// być daleko
// VelSignal=v; //nie do końca tak, to jest druga prędkość; -1 nie
// wpisywać...
if (VelSignal == 0.0)
VelSignal = -1.0; // aby stojący ruszył
if (sSpeedTable[i].fDist < 0.0) // jeśli przejechany
{
if (v != 0 ? VelSignal = -1.0 : VelSignal = 0.0)
; // ustawienie, gdy przejechany jest lepsze niż
// wcale, ale to jeszcze nie to
if (sSpeedTable[i].iFlags & spEvent) // jeśli event
if ((sSpeedTable[i].evEvent != eSignSkip) ?
true :
(sSpeedTable[i].fVelNext != 0.0)) // ale inny niż ten,
// na którym minięto
// S1, chyba że się
// już zmieniło
iDrivigFlags &= ~moveVisibility; // sygnał zezwalający na
// jazdę wyłącza jazdę na
// widoczność (S1 na SBL)
// usunąć jeśli nie jest ograniczeniem prędkości
sSpeedTable[i].iFlags =
0; // to można usunąć (nie mogą być usuwane w skanowaniu)
}
}
else if (sSpeedTable[i].evEvent->StopCommand())
{ // jeśli prędkość jest zerowa, a komórka zawiera komendę
eSignNext = sSpeedTable[i].evEvent; // dla informacji
if (iDrivigFlags &
moveStopHere) // jeśli ma stać, dostaje komendę od razu
go = cm_Command; // komenda z komórki, do wykonania po zatrzymaniu
else if (sSpeedTable[i].fDist <= 20.0) // jeśli ma dociągnąć, to niech
// dociąga (moveStopCloser
// dotyczy dociągania do W4, nie
// semafora)
go = cm_Command; // komenda z komórki, do wykonania po zatrzymaniu
}
} // jeśli nie ma zawalidrogi
} // jeśli event
if (v >= 0.0)
{ // pozycje z prędkością -1 można spokojnie pomijać
d = sSpeedTable[i].fDist;
if ((sSpeedTable[i].iFlags & spElapsed) ?
false :
d > 0.0) // sygnał lub ograniczenie z przodu (+32=przejechane)
{ // 2014-02: jeśli stoi, a ma do przejechania kawałek, to niech jedzie
if ((mvOccupied->Vel == 0.0) ?
((sSpeedTable[i].iFlags &
(spEnabled | spEvent | spPassengerStopPoint)) ==
(spEnabled | spEvent | spPassengerStopPoint)) &&
(d > fMaxProximityDist) :
false)
a = (iDrivigFlags & moveStopCloser) ? fAcc : 0.0; // ma podjechać bliżej -
// czy na pewno w tym
// miejscu taki warunek?
else
{
a = (v * v - mvOccupied->Vel * mvOccupied->Vel) /
(25.92 * d); // przyspieszenie: ujemne, gdy trzeba hamować
if (d < fMinProximityDist) // jak jest już blisko
if (v < fVelDes)
fVelDes = v; // ograniczenie aktualnej prędkości
}
}
else if (sSpeedTable[i].iFlags & spTrack) // jeśli tor
{ // tor ogranicza prędkość, dopóki cały skład nie przejedzie,
// d=fLength+d; //zamiana na długość liczoną do przodu
if (v >= 1.0) // EU06 się zawieszało po dojechaniu na koniec toru postojowego
if (d < -fLength)
continue; // zapętlenie, jeśli już wyjechał za ten odcinek
if (v < fVelDes)
fVelDes =
v; // ograniczenie aktualnej prędkości aż do wyjechania za ograniczenie
// if (v==0.0) fAcc=-0.9; //hamowanie jeśli stop
continue; // i tyle wystarczy
}
else // event trzyma tylko jeśli VelNext=0, nawet po przejechaniu (nie powinno
// dotyczyć samochodów?)
a = (v == 0.0 ? -1.0 : fAcc); // ruszanie albo hamowanie
if (a < fAcc && v == Min0R(v, fNext))
{ // mniejsze przyspieszenie to mniejsza możliwość rozpędzenia się albo konieczność
// hamowania
// jeśli droga wolna, to może być a>1.0 i się tu nie załapuje
// if (mvOccupied->Vel>10.0)
fAcc = a; // zalecane przyspieszenie (nie musi być uwzględniane przez AI)
fNext = v; // istotna jest prędkość na końcu tego odcinka
fDist = d; // dlugość odcinka
}
else if ((fAcc > 0) && (v > 0) && (v <= fNext))
{ // jeśli nie ma wskazań do hamowania, można podać drogę i prędkość na jej końcu
fNext = v; // istotna jest prędkość na końcu tego odcinka
fDist = d; // dlugość odcinka (kolejne pozycje mogą wydłużać drogę, jeśli
// prędkość jest stała)
}
} // if (v>=0.0)
if (fNext >= 0.0)
{ // jeśli ograniczenie
if ((sSpeedTable[i].iFlags & (spEnabled | spEvent)) ==
(spEnabled | spEvent)) // tylko sygnał przypisujemy
if (!eSignNext) // jeśli jeszcze nic nie zapisane tam
eSignNext = sSpeedTable[i].evEvent; // dla informacji
if (fNext == 0.0)
break; // nie ma sensu analizować tabelki dalej
}
} // if (sSpeedTable[i].iFlags&1)
} // for
if (VelSignalLast >= 0.0 && !(iDrivigFlags & (moveSemaphorFound | moveSwitchFound)) &&
(OrderCurrentGet() & Obey_train))
VelSignalLast = -1.0; // jeśli mieliśmy ograniczenie z semafora i nie ma przed nami
if (VelSignalLast >= 0.0) //analiza spisanych z tabelki ograniczeń i nadpisanie aktualnego
fVelDes = Min0R(fVelDes, VelSignalLast);
if (VelLimitLast >= 0.0)
fVelDes = Min0R(fVelDes, VelLimitLast);
if (VelRoad >= 0.0)
fVelDes = Min0R(fVelDes, VelRoad);
// nastepnego semafora albo zwrotnicy to uznajemy, że mijamy W5
return go;
};
void TController::TablePurger()
{ // odtykacz: usuwa mniej istotne pozycje ze środka tabelki, aby uniknąć zatkania
//(np. brak ograniczenia pomiędzy zwrotnicami, usunięte sygnały, minięte odcinki łuku)
WriteLog("TablePurger: Czyszczenie tableki.");
int i, j, k = iLast - iFirst; // może być 15 albo 16 pozycji, ostatniej nie ma co sprawdzać
if (k < 0)
k += iSpeedTableSize; // ilość pozycji do przeanalizowania
for (i = iFirst; k > 0; --k, i = (i + 1) % iSpeedTableSize)
{ // sprawdzenie rekordów od (iFirst) do (iLast), o ile są istotne
if ((sSpeedTable[i].iFlags & spEnabled) ?
(sSpeedTable[i].fVelNext < 0) && ((sSpeedTable[i].iFlags & 0xAB) == 0xA3) :
true)
{ // jeśli jest to minięty (0x20) tor (0x03) do liczenia cięciw (0x80), a nie zwrotnica
// (0x08)
for (; k > 0; --k, i = (i + 1) % iSpeedTableSize)
sSpeedTable[i] = sSpeedTable[(i + 1) % iSpeedTableSize]; // skopiowanie
WriteLog("Odtykacz usuwa pozycję");
iLast = (iLast - 1 + iSpeedTableSize) % iSpeedTableSize; // cofnięcie z zawinięciem
return;
}
}
// jeśli powyższe odtykane nie pomoże, można usunąć coś więcej, albo powiększyć tabelkę
TSpeedPos *t = new TSpeedPos[iSpeedTableSize + 16]; // zwiększenie
k = iLast - iFirst + 1; // tym razem wszystkie
if (k < 0)
k += iSpeedTableSize; // ilość pozycji do przeanalizowania
for (j = -1, i = iFirst; k > 0; --k)
{ // przepisywanie rekordów iFirst..iLast na 0..k
t[++j] = sSpeedTable[i];
i = (i + 1) % iSpeedTableSize; // kolejna pozycja mogą być zawinięta
}
iFirst = 0; // teraz będzie od zera
iLast = j; // ostatnia
delete[] sSpeedTable; // to już nie potrzebne
sSpeedTable = t; // bo jest nowe
iSpeedTableSize += 16;
// WriteLog("Tabelka powiększona do "+AnsiString(iSpeedTableSize)+" pozycji");
};
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
TController::TController(bool AI, TDynamicObject *NewControll, bool InitPsyche,
bool primary // czy ma aktywnie prowadzić?
)
{
iEngineActive = 0;
LastUpdatedTime = 0.0;
ElapsedTime = 0.0;
// inicjalizacja zmiennych
Psyche = InitPsyche;
VelDesired = 0.0; // prędkosć początkowa
VelforDriver = -1;
LastReactionTime = 0.0;
HelpMeFlag = false;
// fProximityDist=1; //nie używane
ActualProximityDist = 1;
vCommandLocation.x = 0;
vCommandLocation.y = 0;
vCommandLocation.z = 0;
VelSignal = 0.0; // normalnie na początku ma stać, no chyba że jedzie
VelLimit = -1.0; // brak ograniczenia prędkości
VelNext = 120.0;
VelLimitLast = -1.0; // ostatnie ograniczenie bez ograniczenia
VelSignalLast = -1.0; // ostatni semafor też bez ograniczenia
VelRoad = -1.0; // prędkość drogowa bez ograniczenia
AIControllFlag = AI;
pVehicle = NewControll;
ControllingSet(); // utworzenie połączenia do sterowanego pojazdu
pVehicles[0] = pVehicle->GetFirstDynamic(0); // pierwszy w kierunku jazdy (Np. Pc1)
pVehicles[1] = pVehicle->GetFirstDynamic(1); // ostatni w kierunku jazdy (końcówki)
/*
switch (mvOccupied->CabNo)
{
case -1: SendCtrlBroadcast("CabActivisation",1); break;
case 1: SendCtrlBroadcast("CabActivisation",2); break;
default: AIControllFlag:=False; //na wszelki wypadek
}
*/
iDirection = 0;
iDirectionOrder = mvOccupied->CabNo; // 1=do przodu (w kierunku sprzęgu 0)
VehicleName = mvOccupied->Name;
// TrainParams=NewTrainParams;
// if (TrainParams)
// asNextStop=TrainParams->NextStop();
// else
TrainParams = new TTrainParameters("none"); // rozkład jazdy
// OrderCommand="";
// OrderValue=0;
OrdersClear();
MaxVelFlag = false;
MinVelFlag = false; // Ra: to nie jest używane
iDriverFailCount = 0;
Need_TryAgain = false; // true, jeśli druga pozycja w elektryku nie załapała
Need_BrakeRelease = true;
deltalog = 0.05; // 1.0;
if (WriteLogFlag)
{
mkdir("physicslog\\");
LogFile.open(AnsiString("physicslog\\" + VehicleName + ".dat").c_str(),
std::ios::in | std::ios::out | std::ios::trunc);
#if LOGPRESS == 0
LogFile << AnsiString(" Time [s] Velocity [m/s] Acceleration [m/ss] Coupler.Dist[m] "
"Coupler.Force[N] TractionForce [kN] FrictionForce [kN] "
"BrakeForce [kN] BrakePress [MPa] PipePress [MPa] "
"MotorCurrent [A] MCP SCP BCP LBP DmgFlag Command CVal1 CVal2")
.c_str() << "\r\n";
#endif
#if LOGPRESS == 1
LogFile << AnsiString("t\tVel\tAcc\tPP\tVVP\tBP\tBVP\tCVP").c_str() << "\n";
#endif
LogFile.flush();
}
/*
if (WriteLogFlag)
{
assignfile(AILogFile,VehicleName+".txt");
rewrite(AILogFile);
writeln(AILogFile,"AI driver log: started OK");
close(AILogFile);
}
*/
// VelMargin=2; //Controlling->Vmax*0.015;
fWarningDuration = 0.0; // nic do wytrąbienia
WaitingExpireTime = 31.0; // tyle ma czekać, zanim się ruszy
WaitingTime = 0.0;
fMinProximityDist = 30.0; // stawanie między 30 a 60 m przed przeszkodą
fMaxProximityDist = 50.0;
iVehicleCount = -2; // wartość neutralna
// Prepare2press=false; //bez dociskania
eStopReason = stopSleep; // na początku śpi
fLength = 0.0;
fMass = 0.0; //[kg]
eSignNext = NULL; // sygnał zmieniający prędkość, do pokazania na [F2]
fShuntVelocity = 40; // domyślna prędkość manewrowa
fStopTime = 0.0; // czas postoju przed dalszą jazdą (np. na przystanku)
iDrivigFlags = moveStopPoint; // podjedź do W4 możliwie blisko
iDrivigFlags |= moveStopHere; // nie podjeżdżaj do semafora, jeśli droga nie jest wolna
iDrivigFlags |= moveStartHorn; // podaj sygnał po podaniu wolnej drogi
if (primary)
iDrivigFlags |= movePrimary; // aktywnie prowadzące pojazd
Ready = false;
if (mvOccupied->CategoryFlag & 2)
{ // samochody: na podst. http://www.prawko-kwartnik.info/hamowanie.html
// fDriverBraking=0.0065; //mnożone przez (v^2+40*v) [km/h] daje prawie drogę hamowania [m]
fDriverBraking = 0.03; // coś nie hamują te samochody zbyt dobrze
fDriverDist = 5.0; // 5m - zachowywany odstęp przed kolizją
fVelPlus = 10.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania
fVelMinus = 2.0; // margines prędkości powodujący załączenie napędu
}
else
{ // pociągi i statki
fDriverBraking = 0.06; // mnożone przez (v^2+40*v) [km/h] daje prawie drogę hamowania [m]
fDriverDist = 50.0; // 50m - zachowywany odstęp przed kolizją
fVelPlus = 5.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania
fVelMinus = 5.0; // margines prędkości powodujący załączenie napędu
}
SetDriverPsyche(); // na końcu, bo wymaga ustawienia zmiennych
AccDesired = AccPreferred;
fVelMax = -1; // ustalenie prędkości dla składu
fBrakeTime = 0.0; // po jakim czasie przekręcić hamulec
iVehicles = 0; // na wszelki wypadek
iSpeedTableSize = 16;
sSpeedTable = new TSpeedPos[iSpeedTableSize];
TableClear();
iRadioChannel = 1; // numer aktualnego kanału radiowego
fActionTime = 0.0;
eAction = actSleep;
tsGuardSignal = NULL; // komunikat od kierownika
iGuardRadio = 0; // nie przez radio
iStationStart = 0; // nic?
// fAccThreshold może podlegać uczeniu się - hamowanie powinno być rejestrowane, a potem
// analizowane
fAccThreshold =
(mvOccupied->TrainType & dt_EZT) ? -0.6 : -0.2; // próg opóźnienia dla zadziałania hamulca
fLastStopExpDist = -1.0f;
iRouteWanted = 3; // powiedzmy, że ma jechać prosto (1=w lewo)
iCoupler = 0; // sprzęg; niezerowy gdy ma być podłączanie; samo podłączanie w trybie Connect
// (wcześniej może być np. Prepare_engine)
fOverhead1 = 3000.0; // informacja o napięciu w sieci trakcyjnej (0=brak drutu, zatrzymaj!)
fOverhead2 = -1.0; // informacja o sposobie jazdy (-1=normalnie, 0=bez prądu, >0=z opuszczonym i
// ograniczeniem prędkości)
iOverheadZero = 0; // suma bitowa jezdy bezprądowej, bity ustawiane przez pojazdy z
// podniesionymi pantografami
iOverheadDown = 0; // suma bitowa opuszczenia pantografów, bity ustawiane przez pojazdy z
// podniesionymi pantografami
fAccDesiredAv = 0.0; // uśrednione przyspieszenie z kolejnych przebłysków świadomości, żeby
// ograniczyć migotanie
fVoltage = 0.0; // uśrednione napięcie sieci: przy spadku poniżej wartości minimalnej opóźnić
// rozruch o losowy czas
};
void TController::CloseLog()
{
if (WriteLogFlag)
{
LogFile.close();
// if WriteLogFlag)
// CloseFile(AILogFile);
/* append(AIlogFile);
writeln(AILogFile,ElapsedTime5:2,": QUIT");
close(AILogFile); */
}
};
TController::~TController()
{ // wykopanie mechanika z roboty
delete tsGuardSignal;
delete TrainParams;
delete[] sSpeedTable;
CloseLog();
};
AnsiString TController::Order2Str(TOrders Order)
{ // zamiana kodu rozkazu na opis
if (Order & Change_direction)
return "Change_direction"; // może być nałożona na inną i wtedy ma priorytet
if (Order == Wait_for_orders)
return "Wait_for_orders";
if (Order == Prepare_engine)
return "Prepare_engine";
if (Order == Shunt)
return "Shunt";
if (Order == Connect)
return "Connect";
if (Order == Disconnect)
return "Disconnect";
if (Order == Obey_train)
return "Obey_train";
if (Order == Release_engine)
return "Release_engine";
if (Order == Jump_to_first_order)
return "Jump_to_first_order";
/* Ra: wersja ze switch nie działa prawidłowo (czemu?)
switch (Order)
{
Wait_for_orders: return "Wait_for_orders";
Prepare_engine: return "Prepare_engine";
Shunt: return "Shunt";
Change_direction: return "Change_direction";
Obey_train: return "Obey_train";
Release_engine: return "Release_engine";
Jump_to_first_order: return "Jump_to_first_order";
}
*/
return "Undefined!";
}
AnsiString TController::OrderCurrent()
{ // pobranie aktualnego rozkazu celem wyświetlenia
return AnsiString(OrderPos) + ". " + Order2Str(OrderList[OrderPos]);
};
void TController::OrdersClear()
{ // czyszczenie tabeli rozkazów na starcie albo po dojściu do końca
OrderPos = 0;
OrderTop = 1; // szczyt stosu rozkazów
for (int b = 0; b < maxorders; b++)
OrderList[b] = Wait_for_orders;
#if LOGORDERS
WriteLog("--> OrdersClear");
#endif
};
void TController::Activation()
{ // umieszczenie obsady w odpowiednim członie, wykonywane wyłącznie gdy steruje AI
iDirection = iDirectionOrder; // kierunek (względem sprzęgów pojazdu z AI) właśnie został
// ustalony (zmieniony)
if (iDirection)
{ // jeśli jest ustalony kierunek
TDynamicObject *old = pVehicle, *d = pVehicle; // w tym siedzi AI
TController *drugi; // jakby były dwa, to zamienić miejscami, a nie robić wycieku pamięci
// poprzez nadpisanie
int brake = mvOccupied->LocalBrakePos;
while (mvControlling->MainCtrlPos) // samo zapętlenie DecSpeed() nie wystarcza :/
DecSpeed(true); // wymuszenie zerowania nastawnika jazdy
while (mvOccupied->ActiveDir < 0)
mvOccupied->DirectionForward(); // kierunek na 0
while (mvOccupied->ActiveDir > 0)
mvOccupied->DirectionBackward();
if (TestFlag(d->MoverParameters->Couplers[iDirectionOrder < 0 ? 1 : 0].CouplingFlag,
ctrain_controll))
{
mvControlling->MainSwitch(
false); // dezaktywacja czuwaka, jeśli przejście do innego członu
mvOccupied->DecLocalBrakeLevel(10); // zwolnienie hamulca w opuszczanym pojeździe
// mvOccupied->BrakeLevelSet((mvOccupied->BrakeHandle==FVel6)?4:-2); //odcięcie na
// zaworze maszynisty, FVel6 po drugiej stronie nie luzuje
mvOccupied->BrakeLevelSet(
mvOccupied->Handle->GetPos(bh_NP)); // odcięcie na zaworze maszynisty
}
mvOccupied->ActiveCab = mvOccupied->CabNo; // użytkownik moze zmienić ActiveCab wychodząc
mvOccupied->CabDeactivisation(); // tak jest w Train.cpp
// przejście AI na drugą stronę EN57, ET41 itp.
while (TestFlag(d->MoverParameters->Couplers[iDirection < 0 ? 1 : 0].CouplingFlag,
ctrain_controll))
{ // jeśli pojazd z przodu jest ukrotniony, to przechodzimy do niego
d = iDirection * d->DirectionGet() < 0 ? d->Next() :
d->Prev(); // przechodzimy do następnego członu
if (d)
{
drugi = d->Mechanik; // zapamiętanie tego, co ewentualnie tam siedzi, żeby w razie
// dwóch zamienić miejscami
d->Mechanik = this; // na razie bilokacja
d->MoverParameters->SetInternalCommand(
"", 0, 0); // usunięcie ewentualnie zalegającej komendy (Change_direction?)
if (d->DirectionGet() != pVehicle->DirectionGet()) // jeśli są przeciwne do siebie
iDirection = -iDirection; // to będziemy jechać w drugą stronę względem
// zasiedzianego pojazdu
pVehicle->Mechanik = drugi; // wsadzamy tego, co ewentualnie był (podwójna trakcja)
pVehicle->MoverParameters->CabNo = 0; // wyłączanie kabin po drodze
pVehicle->MoverParameters->ActiveCab = 0; // i zaznaczenie, że nie ma tam nikogo
pVehicle = d; // a mechu ma nowy pojazd (no, człon)
}
else
break; // jak koniec składu, to mechanik dalej nie idzie
}
if (pVehicle != old)
{ // jeśli zmieniony został pojazd prowadzony
Global::pWorld->CabChange(old, pVehicle); // ewentualna zmiana kabiny użytkownikowi
ControllingSet(); // utworzenie połączenia do sterowanego pojazdu (może się zmienić) -
// silnikowy dla EZT
}
if (mvControlling->EngineType ==
DieselEngine) // dla 2Ls150 - przed ustawieniem kierunku - można zmienić tryb pracy
if (mvControlling->ShuntModeAllow)
mvControlling->CurrentSwitch(
(OrderList[OrderPos] & Shunt) ||
(fMass > 224000.0)); // do tego na wzniesieniu może nie dać rady na liniowym
// Ra: to przełączanie poniżej jest tu bez sensu
mvOccupied->ActiveCab =
iDirection; // aktywacja kabiny w prowadzonym pojeżdzie (silnikowy może być odwrotnie?)
// mvOccupied->CabNo=iDirection;
// mvOccupied->ActiveDir=0; //żeby sam ustawił kierunek
mvOccupied->CabActivisation(); // uruchomienie kabin w członach
DirectionForward(true); // nawrotnik do przodu
if (brake) // hamowanie tylko jeśli był wcześniej zahamowany (bo możliwe, że jedzie!)
mvOccupied->IncLocalBrakeLevel(brake); // zahamuj jak wcześniej
CheckVehicles(); // sprawdzenie składu, AI zapali światła
TableClear(); // resetowanie tabelki skanowania torów
}
};
void TController::AutoRewident()
{ // autorewident: nastawianie hamulców w składzie
int r = 0, g = 0, p = 0; // ilości wagonów poszczególnych typów
TDynamicObject *d = pVehicles[0]; // pojazd na czele składu
// 1. Zebranie informacji o składzie pociągu — przejście wzdłuż składu i odczyt parametrów:
// · ilość wagonów -> są zliczane, wszystkich pojazdów jest (iVehicles)
// · długość (jako suma) -> jest w (fLength)
// · masa (jako suma) -> jest w (fMass)
while (d)
{ // klasyfikacja pojazdów wg BrakeDelays i mocy (licznik)
if (d->MoverParameters->Power <
1) // - lokomotywa - Power>1 - ale może być nieczynna na końcu...
if (TestFlag(d->MoverParameters->BrakeDelays, bdelay_R))
++r; // - wagon pospieszny - jest R
else if (TestFlag(d->MoverParameters->BrakeDelays, bdelay_G))
++g; // - wagon towarowy - jest G (nie ma R)
else
++p; // - wagon osobowy - reszta (bez G i bez R)
d = d->Next(); // kolejny pojazd, podłączony od tyłu (licząc od czoła)
}
// 2. Określenie typu pociągu i nastawy:
int ustaw; //+16 dla pasażerskiego
if (r + g + p == 0)
ustaw = 16 + bdelay_R; // lokomotywa luzem (może być wieloczłonowa)
else
{ // jeśli są wagony
ustaw = (g < min(4, r + p) ? 16 : 0);
if (ustaw) // jeśli towarowe < Min(4, pospieszne+osobowe)
{ // to skład pasażerski - nastawianie pasażerskiego
ustaw += (g && (r < g + p)) ? bdelay_P : bdelay_R;
// jeżeli towarowe>0 oraz pospiesze<=towarowe+osobowe to P (0)
// inaczej R (2)
}
else
{ // inaczej towarowy - nastawianie towarowego
if ((fLength < 300.0) && (fMass < 600000.0)) //[kg]
ustaw |= bdelay_P; // jeżeli długość<300 oraz masa<600 to P (0)
else if ((fLength < 500.0) && (fMass < 1300000.0))
ustaw |= bdelay_R; // jeżeli długość<500 oraz masa<1300 to GP (2)
else
ustaw |= bdelay_G; // inaczej G (1)
}
// zasadniczo na sieci PKP kilka lat temu na P/GP jeździły tylko kontenerowce o
// rozkładowej 90 km/h. Pozostałe jeździły 70 km/h i były nastawione na G.
}
d = pVehicles[0]; // pojazd na czele składu
p = 0; // będziemy tu liczyć wagony od lokomotywy dla nastawy GP
while (d)
{ // 3. Nastawianie
switch (ustaw)
{
case bdelay_P: // towarowy P - lokomotywa na G, reszta na P.
d->MoverParameters->BrakeDelaySwitch(d->MoverParameters->Power > 1 ? bdelay_G :
bdelay_P);
break;
case bdelay_G: // towarowy G - wszystko na G, jeśli nie ma to P (powinno się wyłączyć
// hamulec)
d->MoverParameters->BrakeDelaySwitch(
TestFlag(d->MoverParameters->BrakeDelays, bdelay_G) ? bdelay_G : bdelay_P);
break;
case bdelay_R: // towarowy GP - lokomotywa oraz 5 pierwszych pojazdów przy niej na G, reszta
// na P
if (d->MoverParameters->Power > 1)
{
d->MoverParameters->BrakeDelaySwitch(bdelay_G);
p = 0; // a jak będzie druga w środku?
}
else
d->MoverParameters->BrakeDelaySwitch(++p <= 5 ? bdelay_G : bdelay_P);
break;
case 16 + bdelay_R: // pasażerski R - na R, jeśli nie ma to P
d->MoverParameters->BrakeDelaySwitch(
TestFlag(d->MoverParameters->BrakeDelays, bdelay_R) ? bdelay_R : bdelay_P);
break;
case 16 + bdelay_P: // pasażerski P - wszystko na P
d->MoverParameters->BrakeDelaySwitch(bdelay_P);
break;
}
d = d->Next(); // kolejny pojazd, podłączony od tyłu (licząc od czoła)
}
};
bool TController::CheckVehicles(TOrders user)
{ // sprawdzenie stanu posiadanych pojazdów w składzie i zapalenie świateł
TDynamicObject *p; // roboczy wskaźnik na pojazd
iVehicles = 0; // ilość pojazdów w składzie
int d = mvOccupied->DirAbsolute; // który sprzęg jest z przodu
if (!d) // jeśli nie ma ustalonego kierunku
d = mvOccupied->CabNo; // to jedziemy wg aktualnej kabiny
iDirection = d; // ustalenie kierunku jazdy (powinno zrobić PrepareEngine?)
d = d >= 0 ? 0 : 1; // kierunek szukania czoła (numer sprzęgu)
pVehicles[0] = p = pVehicle->FirstFind(d); // pojazd na czele składu
// liczenie pojazdów w składzie i ustalenie parametrów
int dir = d = 1 - d; // a dalej będziemy zliczać od czoła do tyłu
fLength = 0.0; // długość składu do badania wyjechania za ograniczenie
fMass = 0.0; // całkowita masa do liczenia stycznej składowej grawitacji
fVelMax = -1; // ustalenie prędkości dla składu
bool main = true; // czy jest głównym sterującym
iDrivigFlags |= moveOerlikons; // zakładamy, że są same Oerlikony
// Ra 2014-09: ustawić moveMultiControl, jeśli wszystkie są w ukrotnieniu (i skrajne mają
// kabinę?)
while (p)
{ // sprawdzanie, czy jest głównym sterującym, żeby nie było konfliktu
if (p->Mechanik) // jeśli ma obsadę
if (p->Mechanik != this) // ale chodzi o inny pojazd, niż aktualnie sprawdzający
if (p->Mechanik->iDrivigFlags & movePrimary) // a tamten ma priorytet
if ((iDrivigFlags & movePrimary) && (mvOccupied->DirAbsolute) &&
(mvOccupied->BrakeCtrlPos >= -1)) // jeśli rządzi i ma kierunek
p->Mechanik->iDrivigFlags &= ~movePrimary; // dezaktywuje tamtego
else
main = false; // nici z rządzenia
++iVehicles; // jest jeden pojazd więcej
pVehicles[1] = p; // zapamiętanie ostatniego
fLength += p->MoverParameters->Dim.L; // dodanie długości pojazdu
fMass += p->MoverParameters->TotalMass; // dodanie masy łącznie z ładunkiem
if (fVelMax < 0 ? true : p->MoverParameters->Vmax < fVelMax)
fVelMax = p->MoverParameters->Vmax; // ustalenie maksymalnej prędkości dla składu
/* //youBy: bez przesady, to jest proteza, napelniac mozna, a nawet trzeba, ale z umiarem!
//uwzględnić jeszcze wyłączenie hamulca
if
((p->MoverParameters->BrakeSystem!=Pneumatic)&&(p->MoverParameters->BrakeSystem!=ElectroPneumatic))
iDrivigFlags&=~moveOerlikons; //no jednak nie
else if (p->MoverParameters->BrakeSubsystem!=Oerlikon)
iDrivigFlags&=~moveOerlikons; //wtedy też nie */
p = p->Neightbour(dir); // pojazd podłączony od wskazanej strony
}
if (main)
iDrivigFlags |= movePrimary; // nie znaleziono innego, można się porządzić
/* //tabelka z listą pojazdów jest na razie nie potrzebna
delete[] pVehicles;
pVehicles=new TDynamicObject*[iVehicles];
*/
ControllingSet(); // ustalenie członu do sterowania (może być inny niż zasiedziany)
int pantmask = 1;
if (iDrivigFlags & movePrimary)
{ // jeśli jest aktywnie prowadzącym pojazd, może zrobić własny porządek
p = pVehicles[0];
while (p)
{
if (TrainParams)
if (p->asDestination == "none")
p->DestinationSet(TrainParams->Relation2); // relacja docelowa, jeśli nie było
if (AIControllFlag) // jeśli prowadzi komputer
p->RaLightsSet(0, 0); // gasimy światła
if (p->MoverParameters->EnginePowerSource.SourceType == CurrentCollector)
{ // jeśli pojazd posiada pantograf, to przydzielamy mu maskę, którą będzie informował o
// jeździe bezprądowej
p->iOverheadMask = pantmask;
pantmask << 1; // przesunięcie bitów, max. 32 pojazdy z pantografami w składzie
}
d = p->DirectionSet(d ? 1 : -1); // zwraca położenie następnego (1=zgodny,0=odwrócony -
// względem czoła składu)
p->fScanDist = 300.0; // odległość skanowania w poszukiwaniu innych pojazdów
p->ctOwner = this; // dominator oznacza swoje terytorium
p = p->Next(); // pojazd podłączony od tyłu (licząc od czoła)
}
if (AIControllFlag)
{ // jeśli prowadzi komputer
if (OrderCurrentGet() == Obey_train) // jeśli jazda pociągowa
{
Lights(1 + 4 + 16, 2 + 32 + 64); //światła pociągowe (Pc1) i końcówki (Pc5)
#if LOGPRESS == 0
AutoRewident(); // nastawianie hamulca do jazdy pociągowej
#endif
}
else if (OrderCurrentGet() & (Shunt | Connect))
{
Lights(16, (pVehicles[1]->MoverParameters->CabNo) ?
1 :
0); //światła manewrowe (Tb1) na pojeździe z napędem
if (OrderCurrentGet() & Connect) // jeśli łączenie, skanować dalej
pVehicles[0]->fScanDist =
5000.0; // odległość skanowania w poszukiwaniu innych pojazdów
}
else if (OrderCurrentGet() == Disconnect)
if (mvOccupied->ActiveDir > 0) // jak ma kierunek do przodu
Lights(16, 0); //światła manewrowe (Tb1) tylko z przodu, aby nie pozostawić
// odczepionego ze światłem
else // jak dociska
Lights(0, 16); //światła manewrowe (Tb1) tylko z przodu, aby nie pozostawić
// odczepionego ze światłem
}
else // Ra 2014-02: lepiej tu niż w pętli obsługującej komendy, bo tam się zmieni informacja
// o składzie
switch (user) // gdy człowiek i gdy nastąpiło połącznie albo rozłączenie
{
case Change_direction:
while (OrderCurrentGet() & (Change_direction))
JumpToNextOrder(); // zmianę kierunku też można olać, ale zmienić kierunek
// skanowania!
break;
case Connect:
while (OrderCurrentGet() & (Change_direction))
JumpToNextOrder(); // zmianę kierunku też można olać, ale zmienić kierunek
// skanowania!
if (OrderCurrentGet() & (Connect))
{ // jeśli miało być łączenie, zakładamy, że jest dobrze (sprawdzić?)
iCoupler = 0; // koniec z doczepianiem
iDrivigFlags &= ~moveConnect; // zdjęcie flagi doczepiania
JumpToNextOrder(); // wykonanie następnej komendy
if (OrderCurrentGet() & (Change_direction))
JumpToNextOrder(); // zmianę kierunku też można olać, ale zmienić kierunek
// skanowania!
}
break;
case Disconnect:
while (OrderCurrentGet() & (Change_direction))
JumpToNextOrder(); // zmianę kierunku też można olać, ale zmienić kierunek
// skanowania!
if (OrderCurrentGet() & (Disconnect))
{ // wypadało by sprawdzić, czy odczepiono wagony w odpowiednim miejscu
// (iVehicleCount)
JumpToNextOrder(); // wykonanie następnej komendy
if (OrderCurrentGet() & (Change_direction))
JumpToNextOrder(); // zmianę kierunku też można olać, ale zmienić kierunek
// skanowania!
}
}
// Ra 2014-09: tymczasowo prymitywne ustawienie warunku pod kątem SN61
if ((mvOccupied->TrainType == dt_EZT) || (iVehicles == 1))
iDrivigFlags |= movePushPull; // zmiana czoła przez zmianę kabiny
else
iDrivigFlags &= ~movePushPull; // zmiana czoła przez manewry
} // blok wykonywany, gdy aktywnie prowadzi
return true;
}
void TController::Lights(int head, int rear)
{ // zapalenie świateł w skłądzie
pVehicles[0]->RaLightsSet(head, -1); // zapalenie przednich w pierwszym
pVehicles[1]->RaLightsSet(-1, rear); // zapalenie końcówek w ostatnim
}
void TController::DirectionInitial()
{ // ustawienie kierunku po wczytaniu trainset (może jechać na wstecznym
mvOccupied->CabActivisation(); // załączenie rozrządu (wirtualne kabiny)
if (mvOccupied->Vel > 0.0)
{ // jeśli na starcie jedzie
iDirection = iDirectionOrder =
(mvOccupied->V > 0 ? 1 : -1); // początkowa prędkość wymusza kierunek jazdy
DirectionForward(mvOccupied->V * mvOccupied->CabNo >= 0.0); // a dalej ustawienie nawrotnika
}
CheckVehicles(); // sprawdzenie świateł oraz skrajnych pojazdów do skanowania
};
int TController::OrderDirectionChange(int newdir, TMoverParameters *Vehicle)
{ // zmiana kierunku jazdy, niezależnie od kabiny
int testd = newdir;
if (Vehicle->Vel < 0.5)
{ // jeśli prawie stoi, można zmienić kierunek, musi być wykonane dwukrotnie, bo za pierwszym
// razem daje na zero
switch (newdir * Vehicle->CabNo)
{ // DirectionBackward() i DirectionForward() to zmiany względem kabiny
case -1: // if (!Vehicle->DirectionBackward()) testd=0; break;
DirectionForward(false);
break;
case 1: // if (!Vehicle->DirectionForward()) testd=0; break;
DirectionForward(true);
break;
}
if (testd == 0)
VelforDriver = -1; // kierunek został zmieniony na żądany, można jechać
}
else // jeśli jedzie
VelforDriver = 0; // ma się zatrzymać w celu zmiany kierunku
if ((Vehicle->ActiveDir == 0) && (VelforDriver < Vehicle->Vel)) // Ra: to jest chyba bez sensu
IncBrake(); // niech hamuje
if (Vehicle->ActiveDir == testd * Vehicle->CabNo)
VelforDriver = -1; // można jechać, bo kierunek jest zgodny z żądanym
if (Vehicle->TrainType == dt_EZT)
if (Vehicle->ActiveDir > 0)
// if () //tylko jeśli jazda pociągowa (tego nie wiemy w momencie odpalania silnika)
Vehicle->DirectionForward(); // Ra: z przekazaniem do silnikowego
return (int)VelforDriver; // zwraca prędkość mechanika
}
void TController::WaitingSet(double Seconds)
{ // ustawienie odczekania po zatrzymaniu (ustawienie w trakcie jazdy zatrzyma)
fStopTime = -Seconds; // ujemna wartość oznacza oczekiwanie (potem >=0.0)
}
void TController::SetVelocity(double NewVel, double NewVelNext, TStopReason r)
{ // ustawienie nowej prędkości
WaitingTime = -WaitingExpireTime; // przypisujemy -WaitingExpireTime, a potem porównujemy z
// zerem
MaxVelFlag = False; // Ra: to nie jest używane
MinVelFlag = False; // Ra: to nie jest używane
/* nie używane
if ((NewVel>NewVelNext) //jeśli oczekiwana większa niż następna
|| (NewVel<mvOccupied->Vel)) //albo aktualna jest mniejsza niż aktualna
fProximityDist=-800.0; //droga hamowania do zmiany prędkości
else
fProximityDist=-300.0; //Ra: ujemne wartości są ignorowane
*/
if (NewVel == 0.0) // jeśli ma stanąć
{
if (r != stopNone) // a jest powód podany
eStopReason = r; // to zapamiętać nowy powód
}
else
{
eStopReason = stopNone; // podana prędkość, to nie ma powodów do stania
// to całe poniżej to warunki zatrąbienia przed ruszeniem
if (OrderList[OrderPos] ?
OrderList[OrderPos] & (Obey_train | Shunt | Connect | Prepare_engine) :
true) // jeśli jedzie w dowolnym trybie
if ((mvOccupied->Vel <
1.0)) // jesli stoi (na razie, bo chyba powinien też, gdy hamuje przed semaforem)
if (iDrivigFlags & moveStartHorn) // jezeli trąbienie włączone
if (!(iDrivigFlags & (moveStartHornDone | moveConnect))) // jeśli nie zatrąbione
// i nie jest to moment
// podłączania składu
if (mvOccupied->CategoryFlag & 1) // tylko pociągi trąbią (unimogi tylko na
// torach, więc trzeba raczej sprawdzać
// tor)
if ((NewVel >= 1.0) || (NewVel < 0.0)) // o ile prędkość jest znacząca
{ // fWarningDuration=0.3; //czas trąbienia
// if (AIControllFlag) //jak siedzi krasnoludek, to włączy trąbienie
// mvOccupied->WarningSignal=pVehicle->iHornWarning; //wysokość tonu
// (2=wysoki)
// iDrivigFlags|=moveStartHornDone; //nie trąbić aż do ruszenia
iDrivigFlags |= moveStartHornNow; // zatrąb po odhamowaniu
}
}
VelSignal = NewVel; // prędkość zezwolona na aktualnym odcinku
VelNext = NewVelNext; // prędkość przy następnym obiekcie
}
/* //funkcja do niczego nie potrzebna (ew. do przesunięcia pojazdu o odległość NewDist)
bool TController::SetProximityVelocity(double NewDist,double NewVelNext)
{//informacja o prędkości w pewnej odległości
#if 0
if (NewVelNext==0.0)
WaitingTime=0.0; //nie trzeba już czekać
//if ((NewVelNext>=0)&&((VelNext>=0)&&(NewVelNext<VelNext))||(NewVelNext<VelSignal))||(VelNext<0))
{MaxVelFlag=False; //Ra: to nie jest używane
MinVelFlag=False; //Ra: to nie jest używane
VelNext=NewVelNext;
fProximityDist=NewDist; //dodatnie: przeliczyć do punktu; ujemne: wziąć dosłownie
return true;
}
//else return false
#endif
}
*/
void TController::SetDriverPsyche()
{
// double maxdist=0.5; //skalowanie dystansu od innego pojazdu, zmienic to!!!
if ((Psyche == Aggressive) && (OrderList[OrderPos] == Obey_train))
{
ReactionTime = HardReactionTime; // w zaleznosci od charakteru maszynisty
// if (pOccupied)
if (mvOccupied->CategoryFlag & 2)
{
WaitingExpireTime = 1; // tyle ma czekać samochód, zanim się ruszy
AccPreferred = 3.0; //[m/ss] agresywny
}
else
{
WaitingExpireTime = 61; // tyle ma czekać, zanim się ruszy
AccPreferred = HardAcceleration; // agresywny
}
}
else
{
ReactionTime = EasyReactionTime; // spokojny
if (mvOccupied->CategoryFlag & 2)
{
WaitingExpireTime = 3; // tyle ma czekać samochód, zanim się ruszy
AccPreferred = 2.0; //[m/ss]
}
else
{
WaitingExpireTime = 65; // tyle ma czekać, zanim się ruszy
AccPreferred = EasyAcceleration;
}
}
if (mvControlling && mvOccupied)
{ // with Controlling do
if (mvControlling->MainCtrlPos < 3)
ReactionTime = mvControlling->InitialCtrlDelay + ReactionTime;
if (mvOccupied->BrakeCtrlPos > 1)
ReactionTime = 0.5 * ReactionTime;
/*
if (mvOccupied->Vel>0.1) //o ile jedziemy
{//sprawdzenie jazdy na widoczność
TCoupling
*c=pVehicles[0]->MoverParameters->Couplers+(pVehicles[0]->DirectionGet()>0?0:1); //sprzęg
z przodu składu
if (c->Connected) //a mamy coś z przodu
if (c->CouplingFlag==0) //jeśli to coś jest podłączone sprzęgiem wirtualnym
{//wyliczanie optymalnego przyspieszenia do jazdy na widoczność (Ra: na pewno tutaj?)
double k=c->Connected->Vel; //prędkość pojazdu z przodu (zakładając, że jedzie w tę
samą stronę!!!)
if (k<=mvOccupied->Vel) //porównanie modułów prędkości [km/h]
{if (pVehicles[0]->fTrackBlock<fMaxProximityDist) //porównianie z minimalną odległością
kolizyjną
k=-AccPreferred; //hamowanie maksymalne, bo jest za blisko
else
{//jeśli tamten jedzie szybciej, to nie potrzeba modyfikować przyspieszenia
double d=(pVehicles[0]->fTrackBlock-0.5*fabs(mvOccupied->V)-fMaxProximityDist);
//bezpieczna odległość za poprzednim
//a=(v2*v2-v1*v1)/(25.92*(d-0.5*v1))
//(v2*v2-v1*v1)/2 to różnica energii kinetycznych na jednostkę masy
//jeśli v2=50km/h,v1=60km/h,d=200m => k=(192.9-277.8)/(25.92*(200-0.5*16.7)=-0.0171
[m/s^2]
//jeśli v2=50km/h,v1=60km/h,d=100m => k=(192.9-277.8)/(25.92*(100-0.5*16.7)=-0.0357
[m/s^2]
//jeśli v2=50km/h,v1=60km/h,d=50m => k=(192.9-277.8)/(25.92*( 50-0.5*16.7)=-0.0786
[m/s^2]
//jeśli v2=50km/h,v1=60km/h,d=25m => k=(192.9-277.8)/(25.92*( 25-0.5*16.7)=-0.1967
[m/s^2]
if (d>0) //bo jak ujemne, to zacznie przyspieszać, aby się zderzyć
k=(k*k-mvOccupied->Vel*mvOccupied->Vel)/(25.92*d); //energia kinetyczna dzielona
przez masę i drogę daje przyspieszenie
else
k=0.0; //może lepiej nie przyspieszać -AccPreferred; //hamowanie
//WriteLog(pVehicle->asName+" "+AnsiString(k));
}
if (d<fBrakeDist) //bo z daleka nie ma co hamować
AccPreferred=Min0R(k,AccPreferred);
}
}
}
*/
}
};
bool TController::PrepareEngine()
{ // odpalanie silnika
bool OK;
bool voltfront, voltrear;
voltfront = false;
voltrear = false;
LastReactionTime = 0.0;
ReactionTime = PrepareTime;
iDrivigFlags |= moveActive; // może skanować sygnały i reagować na komendy
// with Controlling do
if (((mvControlling->EnginePowerSource.SourceType ==
CurrentCollector) /*||(mvOccupied->TrainType==dt_EZT)*/))
{
if (mvControlling
->GetTrainsetVoltage()) // sprawdzanie, czy zasilanie jest może w innym członie
{
voltfront = true;
voltrear = true;
}
}
// begin
// if Couplers[0].Connected<>nil)
// begin
// if Couplers[0].Connected^.PantFrontVolt or Couplers[0].Connected^.PantRearVolt)
// voltfront:=true
// else
// voltfront:=false;
// end
// else
// voltfront:=false;
// if Couplers[1].Connected<>nil)
// begin
// if Couplers[1].Connected^.PantFrontVolt or Couplers[1].Connected^.PantRearVolt)
// voltrear:=true
// else
// voltrear:=false;
// end
// else
// voltrear:=false;
// end
else
// if EnginePowerSource.SourceType<>CurrentCollector)
if (mvOccupied->TrainType != dt_EZT)
voltfront = true; // Ra 2014-06: to jest wirtualny prąd dla spalinowych???
if (AIControllFlag) // jeśli prowadzi komputer
{ // część wykonawcza dla sterowania przez komputer
mvOccupied->BatterySwitch(true);
if (mvControlling->EnginePowerSource.SourceType == CurrentCollector)
{ // jeśli silnikowy jest pantografującym
if (mvControlling->PantPress > 4.3)
{ // jeżeli jest wystarczające ciśnienie w pantografach
if ((!mvControlling->bPantKurek3) ||
(mvControlling->PantPress <=
mvControlling->ScndPipePress)) // kurek przełączony albo główna już pompuje
mvControlling->PantCompFlag = false; // sprężarkę pantografów można już wyłączyć
mvControlling->PantFront(true);
mvControlling->PantRear(true);
}
else if (mvControlling->PantPress < 4.2) //żeby nie załączał zaraz po przekroczeniu 4.0
{ // załączenie małej sprężarki
mvControlling->bPantKurek3 =
false; // odłączenie zbiornika głównego, bo z nim nie da rady napompować
mvControlling->PantCompFlag = true; // załączenie sprężarki pantografów
}
}
// if (mvOccupied->TrainType==dt_EZT)
//{//Ra 2014-12: po co to tutaj?
// mvControlling->PantFront(true);
// mvControlling->PantRear(true);
//}
// if (mvControlling->EngineType==DieselElectric)
// mvControlling->Battery=true; //Ra: to musi być tak?
}
if (mvControlling->PantFrontVolt || mvControlling->PantRearVolt || voltfront || voltrear)
{ // najpierw ustalamy kierunek, jeśli nie został ustalony
if (!iDirection) // jeśli nie ma ustalonego kierunku
if (mvOccupied->V == 0)
{ // ustalenie kierunku, gdy stoi
iDirection = mvOccupied->CabNo; // wg wybranej kabiny
if (!iDirection) // jeśli nie ma ustalonego kierunku
if ((mvControlling->PantFrontVolt != 0.0) ||
(mvControlling->PantRearVolt != 0.0) || voltfront || voltrear)
{
if (mvOccupied->Couplers[1].CouplingFlag ==
ctrain_virtual) // jeśli z tyłu nie ma nic
iDirection = -1; // jazda w kierunku sprzęgu 1
if (mvOccupied->Couplers[0].CouplingFlag ==
ctrain_virtual) // jeśli z przodu nie ma nic
iDirection = 1; // jazda w kierunku sprzęgu 0
}
}
else // ustalenie kierunku, gdy jedzie
if ((mvControlling->PantFrontVolt != 0.0) || (mvControlling->PantRearVolt != 0.0) ||
voltfront || voltrear)
if (mvOccupied->V < 0) // jedzie do tyłu
iDirection = -1; // jazda w kierunku sprzęgu 1
else // jak nie do tyłu, to do przodu
iDirection = 1; // jazda w kierunku sprzęgu 0
if (AIControllFlag) // jeśli prowadzi komputer
{ // część wykonawcza dla sterowania przez komputer
if (mvControlling->ConvOvldFlag)
{ // wywalił bezpiecznik nadmiarowy przetwornicy
while (DecSpeed(true))
; // zerowanie napędu
mvControlling->ConvOvldFlag = false; // reset nadmiarowego
}
else if (!mvControlling->Mains)
{
// if TrainType=dt_SN61)
// begin
// OK:=(OrderDirectionChange(ChangeDir,Controlling)=-1);
// OK:=IncMainCtrl(1);
// end;
while (DecSpeed(true))
; // zerowanie napędu
OK = mvControlling->MainSwitch(true);
if (mvControlling->EngineType == DieselEngine)
{ // Ra 2014-06: dla SN61 trzeba wrzucić pierwszą pozycję - nie wiem, czy tutaj...
// kiedyś działało...
if (!mvControlling->MainCtrlPos)
{
if (mvControlling->RList[0].R ==
0.0) // gdy na pozycji 0 dawka paliwa jest zerowa, to zgaśnie
mvControlling->IncMainCtrl(1); // dlatego trzeba zwiększyć pozycję
if (!mvControlling->ScndCtrlPos) // jeśli bieg nie został ustawiony
if (!mvControlling->MotorParam[0].AutoSwitch) // gdy biegi ręczne
if (mvControlling->MotorParam[0].mIsat == 0.0) // bl,mIsat,fi,mfi
mvControlling->IncScndCtrl(1); // pierwszy bieg
}
}
}
else
{ // Ra: iDirection określa, w którą stronę jedzie skład względem sprzęgów pojazdu z AI
OK = (OrderDirectionChange(iDirection, mvOccupied) == -1);
// w EN57 sprężarka w ra jest zasilana z silnikowego
mvControlling->CompressorSwitch(true);
mvControlling->ConverterSwitch(true);
mvControlling->CompressorSwitch(true);
}
}
else
OK = mvControlling->Mains;
}
else
OK = false;
OK = OK && (mvOccupied->ActiveDir != 0) && (mvControlling->CompressorAllow);
if (OK)
{
if (eStopReason == stopSleep) // jeśli dotychczas spał
eStopReason == stopNone; // teraz nie ma powodu do stania
iEngineActive = 1;
return true;
}
else
{
iEngineActive = 0;
return false;
}
};
bool TController::ReleaseEngine()
{ // wyłączanie silnika (test wyłączenia, a część wykonawcza tylko jeśli steruje komputer)
bool OK = false;
LastReactionTime = 0.0;
ReactionTime = PrepareTime;
if (AIControllFlag)
{ // jeśli steruje komputer
if (mvOccupied->DoorOpenCtrl == 1)
{ // zamykanie drzwi
if (mvOccupied->DoorLeftOpened)
mvOccupied->DoorLeft(false);
if (mvOccupied->DoorRightOpened)
mvOccupied->DoorRight(false);
}
if (mvOccupied->ActiveDir == 0)
if (mvControlling->Mains)
{
mvControlling->CompressorSwitch(false);
mvControlling->ConverterSwitch(false);
if (mvControlling->EnginePowerSource.SourceType == CurrentCollector)
{
mvControlling->PantFront(false);
mvControlling->PantRear(false);
}
OK = mvControlling->MainSwitch(false);
}
else
OK = true;
}
else if (mvOccupied->ActiveDir == 0)
OK = mvControlling->Mains; // tylko to testujemy dla pojazdu człowieka
if (AIControllFlag)
if (!mvOccupied->DecBrakeLevel()) // tu moze zmieniać na -2, ale to bez znaczenia
if (!mvOccupied->IncLocalBrakeLevel(1))
{
while (DecSpeed(true))
; // zerowanie nastawników
while (mvOccupied->ActiveDir > 0)
mvOccupied->DirectionBackward();
while (mvOccupied->ActiveDir < 0)
mvOccupied->DirectionForward();
}
OK = OK && (mvOccupied->Vel < 0.01);
if (OK)
{ // jeśli się zatrzymał
iEngineActive = 0;
eStopReason = stopSleep; // stoimy z powodu wyłączenia
eAction = actSleep; //śpi (wygaszony)
if (AIControllFlag)
{
Lights(0, 0); // gasimy światła
mvOccupied->BatterySwitch(false);
}
OrderNext(Wait_for_orders); //żeby nie próbował coś robić dalej
TableClear(); // zapominamy ograniczenia
iDrivigFlags &= ~moveActive; // ma nie skanować sygnałów i nie reagować na komendy
}
return OK;
}
bool TController::IncBrake()
{ // zwiększenie hamowania
bool OK = false;
switch (mvOccupied->BrakeSystem)
{
case Individual:
OK = mvOccupied->IncLocalBrakeLevel(1 + floor(0.5 + fabs(AccDesired)));
break;
case Pneumatic:
if ((mvOccupied->Couplers[0].Connected == NULL) &&
(mvOccupied->Couplers[1].Connected == NULL))
OK = mvOccupied->IncLocalBrakeLevel(
1 + floor(0.5 + fabs(AccDesired))); // hamowanie lokalnym bo luzem jedzie
else
{
if (mvOccupied->BrakeCtrlPos + 1 == mvOccupied->BrakeCtrlPosNo)
{
if (AccDesired < -1.5) // hamowanie nagle
OK = mvOccupied->IncBrakeLevel();
else
OK = false;
}
else
{
/*
if (AccDesired>-0.2) and ((Vel<20) or (Vel-VelNext<10)))
begin
if BrakeCtrlPos>0)
OK:=IncBrakeLevel
else;
OK:=IncLocalBrakeLevel(1); //finezyjne hamowanie lokalnym
end
else
*/
// dodane dla towarowego
if (mvOccupied->BrakeDelayFlag == bdelay_G ?
-AccDesired * 6.6 > Min0R(2, mvOccupied->BrakeCtrlPos) :
true)
{
OK = mvOccupied->IncBrakeLevel();
}
else
OK = false;
}
}
if (mvOccupied->BrakeCtrlPos > 0)
mvOccupied->BrakeReleaser(0);
break;
case ElectroPneumatic:
if (mvOccupied->fBrakeCtrlPos != mvOccupied->Handle->GetPos(bh_EPB))
{
mvOccupied->BrakeLevelSet(mvOccupied->Handle->GetPos(bh_EPB));
if (mvOccupied->Handle->GetPos(bh_EPR) - mvOccupied->Handle->GetPos(bh_EPN) < 0.1)
mvOccupied->SwitchEPBrake(1); // to nie chce działać
OK = true;
}
else
OK = false;
// if (mvOccupied->BrakeCtrlPos<mvOccupied->BrakeCtrlPosNo)
// if
// (mvOccupied->BrakePressureTable[mvOccupied->BrakeCtrlPos+1+2].BrakeType==ElectroPneumatic)
// //+2 to indeks Pascala
// OK=mvOccupied->IncBrakeLevel();
// else
// OK=false;
}
return OK;
}
bool TController::DecBrake()
{ // zmniejszenie siły hamowania
bool OK = false;
switch (mvOccupied->BrakeSystem)
{
case Individual:
OK = mvOccupied->DecLocalBrakeLevel(1 + floor(0.5 + fabs(AccDesired)));
break;
case Pneumatic:
if (mvOccupied->BrakeCtrlPos > 0)
OK = mvOccupied->DecBrakeLevel();
if (!OK)
OK = mvOccupied->DecLocalBrakeLevel(2);
if (mvOccupied->PipePress < 3.0)
Need_BrakeRelease = true;
break;
case ElectroPneumatic:
if (mvOccupied->fBrakeCtrlPos != mvOccupied->Handle->GetPos(bh_EPR))
{
mvOccupied->BrakeLevelSet(mvOccupied->Handle->GetPos(bh_EPR));
if (mvOccupied->Handle->GetPos(bh_EPR) - mvOccupied->Handle->GetPos(bh_EPN) < 0.1)
mvOccupied->SwitchEPBrake(1);
OK = true;
}
else
OK = false;
if (!OK)
OK = mvOccupied->DecLocalBrakeLevel(2);
break;
}
return OK;
};
bool TController::IncSpeed()
{ // zwiększenie prędkości; zwraca false, jeśli dalej się nie da zwiększać
if (tsGuardSignal) // jeśli jest dźwięk kierownika
if (tsGuardSignal->GetStatus() & DSBSTATUS_PLAYING) // jeśli gada, to nie jedziemy
return false;
bool OK = true;
if (iDrivigFlags & moveDoorOpened)
Doors(false); // zamykanie drzwi - tutaj wykonuje tylko AI (zmienia fActionTime)
if (fActionTime < 0.0) // gdy jest nakaz poczekać z jazdą, to nie ruszać
return false;
if (mvControlling->SlippingWheels)
return false; // jak poślizg, to nie przyspieszamy
switch (mvOccupied->EngineType)
{
case None: // McZapkie-041003: wagon sterowniczy
if (mvControlling->MainCtrlPosNo > 0) // jeśli ma czym kręcić
iDrivigFlags |= moveIncSpeed; // ustawienie flagi jazdy
return false;
case ElectricSeriesMotor:
if (mvControlling->EnginePowerSource.SourceType == CurrentCollector) // jeśli pantografujący
{
if (fOverhead2 >= 0.0) // a jazda bezprądowa ustawiana eventami (albo opuszczenie)
return false; // to nici z ruszania
if (iOverheadZero) // jazda bezprądowa z poziomu toru ustawia bity
return false; // to nici z ruszania
}
if (!mvControlling->FuseFlag) //&&mvControlling->StLinFlag) //yBARC
if ((mvControlling->MainCtrlPos == 0) ||
(mvControlling->StLinFlag)) // youBy polecił dodać 2012-09-08 v367
// na pozycji 0 przejdzie, a na pozostałych będzie czekać, aż się załączą liniowe
// (zgaśnie DelayCtrlFlag)
if (Ready || (iDrivigFlags & movePress))
if (fabs(mvControlling->Im) <
(fReady < 0.4 ? mvControlling->Imin : mvControlling->IminLo))
{ // Ra: wywalał nadmiarowy, bo Im może być ujemne; jak nie odhamowany, to nie
// przesadzać z prądem
if ((mvOccupied->Vel <= 30) ||
(mvControlling->Imax > mvControlling->ImaxLo) ||
(fVoltage + fVoltage <
mvControlling->EnginePowerSource.CollectorParameters.MinV +
mvControlling->EnginePowerSource.CollectorParameters.MaxV))
{ // bocznik na szeregowej przy ciezkich bruttach albo przy wysokim rozruchu
// pod górę albo przy niskim napięciu
if (mvControlling->MainCtrlPos ?
mvControlling->RList[mvControlling->MainCtrlPos].R > 0.0 :
true) // oporowa
{
OK = (mvControlling->DelayCtrlFlag ?
true :
mvControlling->IncMainCtrl(1)); // kręcimy nastawnik jazdy
if ((OK) &&
(mvControlling->MainCtrlPos ==
1)) // czekaj na 1 pozycji, zanim się nie włączą liniowe
iDrivigFlags |= moveIncSpeed;
else
iDrivigFlags &= ~moveIncSpeed; // usunięcie flagi czekania
}
else // jeśli bezoporowa (z wyjątekiem 0)
OK = false; // to dać bocznik
}
else
{ // przekroczone 30km/h, można wejść na jazdę równoległą
if (mvControlling->ScndCtrlPos) // jeśli ustawiony bocznik
if (mvControlling->MainCtrlPos <
mvControlling->MainCtrlPosNo - 1) // a nie jest ostatnia pozycja
mvControlling->DecScndCtrl(2); // to bocznik na zero po chamsku
// (ktoś miał to poprawić...)
OK = mvControlling->IncMainCtrl(1);
}
if ((mvControlling->MainCtrlPos > 2) &&
(mvControlling->Im == 0)) // brak prądu na dalszych pozycjach
Need_TryAgain = true; // nie załączona lokomotywa albo wywalił
// nadmiarowy
else if (!OK) // nie da się wrzucić kolejnej pozycji
OK = mvControlling->IncScndCtrl(1); // to dać bocznik
}
mvControlling->AutoRelayCheck(); // sprawdzenie logiki sterowania
break;
case Dumb:
case DieselElectric:
if (!mvControlling->FuseFlag)
if (Ready || (iDrivigFlags & movePress)) //{(BrakePress<=0.01*MaxBrakePress)}
{
OK = mvControlling->IncMainCtrl(1);
if (!OK)
OK = mvControlling->IncScndCtrl(1);
}
break;
case WheelsDriven:
if (!mvControlling->CabNo)
mvControlling->CabActivisation();
if (sin(mvControlling->eAngle) > 0)
mvControlling->IncMainCtrl(3 + 3 * floor(0.5 + fabs(AccDesired)));
else
mvControlling->DecMainCtrl(3 + 3 * floor(0.5 + fabs(AccDesired)));
break;
case DieselEngine:
if (mvControlling->ShuntModeAllow)
{ // dla 2Ls150 można zmienić tryb pracy, jeśli jest w liniowym i nie daje rady (wymaga
// zerowania kierunku)
// mvControlling->ShuntMode=(OrderList[OrderPos]&Shunt)||(fMass>224000.0);
}
if ((mvControlling->Vel > mvControlling->dizel_minVelfullengage) &&
(mvControlling->RList[mvControlling->MainCtrlPos].Mn > 0))
OK = mvControlling->IncMainCtrl(1);
if (mvControlling->RList[mvControlling->MainCtrlPos].Mn == 0)
OK = mvControlling->IncMainCtrl(1);
if (!mvControlling->Mains)
{
mvControlling->MainSwitch(true);
mvControlling->ConverterSwitch(true);
mvControlling->CompressorSwitch(true);
}
break;
}
return OK;
}
bool TController::DecSpeed(bool force)
{ // zmniejszenie prędkości (ale nie hamowanie)
bool OK = false; // domyślnie false, aby wyszło z pętli while
switch (mvOccupied->EngineType)
{
case None: // McZapkie-041003: wagon sterowniczy
iDrivigFlags &= ~moveIncSpeed; // usunięcie flagi jazdy
if (force) // przy aktywacji kabiny jest potrzeba natychmiastowego wyzerowania
if (mvControlling->MainCtrlPosNo > 0) // McZapkie-041003: wagon sterowniczy, np. EZT
mvControlling->DecMainCtrl(1 + (mvControlling->MainCtrlPos > 2 ? 1 : 0));
mvControlling->AutoRelayCheck(); // sprawdzenie logiki sterowania
return false;
case ElectricSeriesMotor:
OK = mvControlling->DecScndCtrl(2); // najpierw bocznik na zero
if (!OK)
OK = mvControlling->DecMainCtrl(1 + (mvControlling->MainCtrlPos > 2 ? 1 : 0));
mvControlling->AutoRelayCheck(); // sprawdzenie logiki sterowania
break;
case Dumb:
case DieselElectric:
OK = mvControlling->DecScndCtrl(2);
if (!OK)
OK = mvControlling->DecMainCtrl(2 + (mvControlling->MainCtrlPos / 2));
break;
case WheelsDriven:
if (!mvControlling->CabNo)
mvControlling->CabActivisation();
if (sin(mvControlling->eAngle) < 0)
mvControlling->IncMainCtrl(3 + 3 * floor(0.5 + fabs(AccDesired)));
else
mvControlling->DecMainCtrl(3 + 3 * floor(0.5 + fabs(AccDesired)));
break;
case DieselEngine:
if ((mvControlling->Vel > mvControlling->dizel_minVelfullengage))
{
if (mvControlling->RList[mvControlling->MainCtrlPos].Mn > 0)
OK = mvControlling->DecMainCtrl(1);
}
else
while ((mvControlling->RList[mvControlling->MainCtrlPos].Mn > 0) &&
(mvControlling->MainCtrlPos > 1))
OK = mvControlling->DecMainCtrl(1);
if (force) // przy aktywacji kabiny jest potrzeba natychmiastowego wyzerowania
OK = mvControlling->DecMainCtrl(1 + (mvControlling->MainCtrlPos > 2 ? 1 : 0));
break;
}
return OK;
};
void TController::SpeedSet()
{ // Ra: regulacja prędkości, wykonywana w każdym przebłysku świadomości AI
// ma dokręcać do bezoporowych i zdejmować pozycje w przypadku przekroczenia prądu
switch (mvOccupied->EngineType)
{
case None: // McZapkie-041003: wagon sterowniczy
if (fActionTime >= -1.0)
mvOccupied->DepartureSignal = false; // trochę niech pobuczy, zanim pojedzie
if (mvControlling->MainCtrlPosNo > 0)
{ // jeśli ma czym kręcić
// TODO: sprawdzanie innego czlonu //if (!FuseFlagCheck())
if ((AccDesired < fAccGravity - 0.05) ||
(mvOccupied->Vel > VelDesired)) // jeśli nie ma przyspieszać
mvControlling->DecMainCtrl(2); // na zero
else if (fActionTime >= 0.0)
{ // jak już można coś poruszać, przetok rozłączać od razu
if (iDrivigFlags & moveIncSpeed)
{ // jak ma jechać
if (fReady < 0.4) // 0.05*Controlling->MaxBrakePress)
{ // jak jest odhamowany
if (mvOccupied->ActiveDir > 0)
mvOccupied->DirectionForward(); //żeby EN57 jechały na drugiej nastawie
{
if (mvControlling->MainCtrlPos &&
!mvControlling
->StLinFlag) // jak niby jedzie, ale ma rozłączone liniowe
mvControlling->DecMainCtrl(
2); // to na zero i czekać na przewalenie kułakowego
else
switch (mvControlling->MainCtrlPos)
{ // ruch nastawnika uzależniony jest od aktualnie ustawionej
// pozycji
case 0:
if (mvControlling->MainCtrlActualPos) // jeśli kułakowy nie jest
// wyzerowany
break; // to czekać na wyzerowanie
mvControlling->IncMainCtrl(1); // przetok; bez "break", bo nie
// ma czekania na 1. pozycji
case 1:
if (VelDesired >= 20)
mvControlling->IncMainCtrl(1); // szeregowa
case 2:
if (VelDesired >= 50)
mvControlling->IncMainCtrl(1); // równoległa
case 3:
if (VelDesired >= 80)
mvControlling->IncMainCtrl(1); // bocznik 1
case 4:
if (VelDesired >= 90)
mvControlling->IncMainCtrl(1); // bocznik 2
case 5:
if (VelDesired >= 100)
mvControlling->IncMainCtrl(1); // bocznik 3
}
if (mvControlling->MainCtrlPos) // jak załączył pozycję
{
fActionTime = -5.0; // niech trochę potrzyma
mvControlling->AutoRelayCheck(); // sprawdzenie logiki sterowania
}
}
}
}
else
{
while (mvControlling->MainCtrlPos)
mvControlling->DecMainCtrl(1); // na zero
fActionTime = -5.0; // niech trochę potrzyma
mvControlling->AutoRelayCheck(); // sprawdzenie logiki sterowania
}
}
}
break;
case ElectricSeriesMotor:
if ((!mvControlling->StLinFlag) && (!mvControlling->DelayCtrlFlag) &&
(!iDrivigFlags & moveIncSpeed)) // styczniki liniowe rozłączone yBARC
// if (iDrivigFlags&moveIncSpeed) {} //jeśli czeka na załączenie liniowych
// else
while (DecSpeed())
; // zerowanie napędu
else if (Ready || (iDrivigFlags & movePress)) // o ile może jechać
if (fAccGravity < -0.10) // i jedzie pod górę większą niż 10 promil
{ // procedura wjeżdżania na ekstremalne wzniesienia
if (fabs(mvControlling->Im) >
0.85 * mvControlling->Imax) // a prąd jest większy niż 85% nadmiarowego
// if (mvControlling->Imin*mvControlling->Voltage/(fMass*fAccGravity)<-2.8) //a
// na niskim się za szybko nie pojedzie
if (mvControlling->Imax * mvControlling->Voltage / (fMass * fAccGravity) <
-2.8) // a na niskim się za szybko nie pojedzie
{ // włączenie wysokiego rozruchu;
// (I*U)[A*V=W=kg*m*m/sss]/(m[kg]*a[m/ss])=v[m/s]; 2.8m/ss=10km/h
if (mvControlling->RList[mvControlling->MainCtrlPos].Bn > 1)
{ // jeśli jedzie na równoległym, to zbijamy do szeregowego, aby włączyć
// wysoki rozruch
if (mvControlling->ScndCtrlPos > 0) // jeżeli jest bocznik
mvControlling->DecScndCtrl(
2); // wyłączyć bocznik, bo może blokować skręcenie NJ
do // skręcanie do bezoporowej na szeregowym
mvControlling->DecMainCtrl(1); // kręcimy nastawnik jazdy o 1 wstecz
while (mvControlling->MainCtrlPos ?
mvControlling->RList[mvControlling->MainCtrlPos].Bn > 1 :
false); // oporowa zapętla
}
if (mvControlling->Imax < mvControlling->ImaxHi) // jeśli da się na wysokim
mvControlling->CurrentSwitch(
true); // rozruch wysoki (za to może się ślizgać)
if (ReactionTime > 0.1)
ReactionTime = 0.1; // orientuj się szybciej
} // if (Im>Imin)
if (fabs(mvControlling->Im) > 0.75 * mvControlling->ImaxHi) // jeśli prąd jest duży
mvControlling->SandDoseOn(); // piaskujemy tory, coby się nie ślizgać
if ((fabs(mvControlling->Im) > 0.96 * mvControlling->Imax) ||
mvControlling->SlippingWheels) // jeśli prąd jest duży (można 690 na 750)
if (mvControlling->ScndCtrlPos > 0) // jeżeli jest bocznik
mvControlling->DecScndCtrl(2); // zmniejszyć bocznik
else
mvControlling->DecMainCtrl(1); // kręcimy nastawnik jazdy o 1 wstecz
}
else // gdy nie jedzie ambitnie pod górę
{ // sprawdzenie, czy rozruch wysoki jest potrzebny
if (mvControlling->Imax > mvControlling->ImaxLo)
if (mvOccupied->Vel >= 30.0) // jak się rozpędził
if (fAccGravity > -0.02) // a i pochylenie mnijsze niż 2‰
mvControlling->CurrentSwitch(false); // rozruch wysoki wyłącz
// dokręcanie do bezoporowej, bo IncSpeed() może nie być wywoływane
// if (mvOccupied->Vel<VelDesired)
// if (AccDesired>-0.1) //nie ma hamować
// if (Controlling->RList[MainCtrlPos].R>0.0)
// if (Im<1.3*Imin) //lekkie przekroczenie miimalnego prądu jest dopuszczalne
// IncMainCtrl(1); //zwieksz nastawnik skoro możesz - tak aby się ustawic na
// bezoporowej
}
break;
case Dumb:
case DieselElectric:
break;
// WheelsDriven :
// begin
// OK:=False;
// end;
case DieselEngine:
// Ra 2014-06: "automatyczna" skrzynia biegów...
if (!mvControlling->MotorParam[mvControlling->ScndCtrlPos].AutoSwitch) // gdy biegi ręczne
if ((mvControlling->ShuntMode ? mvControlling->AnPos : 1.0) * mvControlling->Vel >
0.6 * mvControlling->MotorParam[mvControlling->ScndCtrlPos].mfi)
// if (mvControlling->enrot>0.95*mvControlling->dizel_nMmax) //youBy: jeśli obroty >
// 0,95 nmax, wrzuć wyższy bieg - Ra: to nie działa
{ // jak prędkość większa niż 0.6 maksymalnej na danym biegu, wrzucić wyższy
mvControlling->DecMainCtrl(2);
if (mvControlling->IncScndCtrl(1))
if (mvControlling->MotorParam[mvControlling->ScndCtrlPos].mIsat ==
0.0) // jeśli bieg jałowy
mvControlling->IncScndCtrl(1); // to kolejny
}
else if ((mvControlling->ShuntMode ? mvControlling->AnPos : 1.0) * mvControlling->Vel <
mvControlling->MotorParam[mvControlling->ScndCtrlPos].fi)
{ // jak prędkość mniejsza niż minimalna na danym biegu, wrzucić niższy
mvControlling->DecMainCtrl(2);
mvControlling->DecScndCtrl(1);
if (mvControlling->MotorParam[mvControlling->ScndCtrlPos].mIsat ==
0.0) // jeśli bieg jałowy
if (mvControlling->ScndCtrlPos) // a jeszcze zera nie osiągnięto
mvControlling->DecScndCtrl(1); // to kolejny wcześniejszy
else
mvControlling->IncScndCtrl(1); // a jak zeszło na zero, to powrót
}
break;
}
};
void TController::Doors(bool what)
{ // otwieranie/zamykanie drzwi w składzie albo (tylko AI) EZT
if (what)
{ // otwarcie
}
else
{ // zamykanie
if (mvOccupied->DoorOpenCtrl == 1)
{ // jeśli drzwi sterowane z kabiny
if (AIControllFlag)
if (mvOccupied->DoorLeftOpened || mvOccupied->DoorRightOpened)
{ // AI zamyka drzwi przed odjazdem
if (mvOccupied->DoorClosureWarning)
mvOccupied->DepartureSignal = true; // załącenie bzyczka
mvOccupied->DoorLeft(false); // zamykanie drzwi
mvOccupied->DoorRight(false);
// Ra: trzeba by ustawić jakiś czas oczekiwania na zamknięcie się drzwi
fActionTime = -1.5 - 0.1 * random(10); // czekanie sekundę, może trochę dłużej
iDrivigFlags &= ~moveDoorOpened; // nie wykonywać drugi raz
}
}
else
{ // jeśli nie, to zamykanie w składzie wagonowym
TDynamicObject *p = pVehicles[0]; // pojazd na czole składu
while (p)
{ // zamykanie drzwi w pojazdach - flaga zezwolenia była by lepsza
p->MoverParameters->DoorLeft(false); // w lokomotywie można by nie zamykać...
p->MoverParameters->DoorRight(false);
p = p->Next(); // pojazd podłączony z tyłu (patrząc od czoła)
}
// WaitingSet(5); //10 sekund tu to za długo, opóźnia odjazd o pół minuty
fActionTime = -1.5 - 0.1 * random(10); // czekanie sekundę, może trochę dłużej
iDrivigFlags &= ~moveDoorOpened; // zostały zamknięte - nie wykonywać drugi raz
}
}
};
void TController::RecognizeCommand()
{ // odczytuje i wykonuje komendę przekazaną lokomotywie
TCommand *c = &mvOccupied->CommandIn;
PutCommand(c->Command, c->Value1, c->Value2, c->Location, stopComm);
c->Command = ""; // usunięcie obsłużonej komendy
}
void TController::PutCommand(AnsiString NewCommand, double NewValue1, double NewValue2,
const TLocation &NewLocation, TStopReason reason)
{ // wysłanie komendy przez event PutValues, jak pojazd ma obsadę, to wysyła tutaj, a nie do pojazdu
// bezpośrednio
vector3 sl;
sl.x = -NewLocation.X; // zamiana na współrzędne scenerii
sl.z = NewLocation.Y;
sl.y = NewLocation.Z;
if (!PutCommand(NewCommand, NewValue1, NewValue2, &sl, reason))
mvOccupied->PutCommand(NewCommand, NewValue1, NewValue2, NewLocation);
}
bool TController::PutCommand(AnsiString NewCommand, double NewValue1, double NewValue2,
const vector3 *NewLocation, TStopReason reason)
{ // analiza komendy
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
// Ra 2014-04: jednak przeniosłem do rozrządczego
mvOccupied->PutCommand(NewCommand, NewValue1, NewValue2, mvOccupied->Loc);
mvOccupied->RunInternalCommand(); // rozpoznaj komende bo lokomotywa jej nie rozpoznaje
return true; // załatwione
}
if (NewCommand == "Overhead")
{ // informacja o stanie sieci trakcyjnej
fOverhead1 =
NewValue1; // informacja o napięciu w sieci trakcyjnej (0=brak drutu, zatrzymaj!)
fOverhead2 = NewValue2; // informacja o sposobie jazdy (-1=normalnie, 0=bez prądu, >0=z
// opuszczonym i ograniczeniem prędkości)
return true; // załatwione
}
else if (NewCommand == "Emergency_brake") // wymuszenie zatrzymania, niezależnie kto prowadzi
{ // Ra: no nadal nie jest zbyt pięknie
SetVelocity(0, 0, reason);
mvOccupied->PutCommand("Emergency_brake", 1.0, 1.0, mvOccupied->Loc);
return true; // załatwione
}
else if (NewCommand.Pos("Timetable:") == 1)
{ // przypisanie nowego rozkładu jazdy, również prowadzonemu przez użytkownika
NewCommand.Delete(1, 10); // zostanie nazwa pliku z rozkładem
#if LOGSTOPS
WriteLog("New timetable for " + pVehicle->asName + ": " + NewCommand); // informacja
#endif
if (!TrainParams)
TrainParams = new TTrainParameters(NewCommand); // rozkład jazdy
else
TrainParams->NewName(NewCommand); // czyści tabelkę przystanków
delete tsGuardSignal;
tsGuardSignal = NULL; // wywalenie kierownika
if (NewCommand != "none")
{
if (!TrainParams->LoadTTfile(
Global::asCurrentSceneryPath, floor(NewValue2 + 0.5),
NewValue1)) // pierwszy parametr to przesunięcie rozkładu w czasie
{
if (ConversionError == -8)
ErrorLog("Missed timetable: " + NewCommand);
WriteLog("Cannot load timetable file " + NewCommand + "\r\nError " +
ConversionError + " in position " + TrainParams->StationCount);
NewCommand = ""; // puste, dla wymiennej tekstury
}
else
{ // inicjacja pierwszego przystanku i pobranie jego nazwy
TrainParams->UpdateMTable(GlobalTime->hh, GlobalTime->mm,
TrainParams->NextStationName);
TrainParams->StationIndexInc(); // przejście do następnej
iStationStart = TrainParams->StationIndex;
asNextStop = TrainParams->NextStop();
iDrivigFlags |= movePrimary; // skoro dostał rozkład, to jest teraz głównym
NewCommand = Global::asCurrentSceneryPath + NewCommand + ".wav"; // na razie jeden
if (FileExists(NewCommand))
{ // wczytanie dźwięku odjazdu podawanego bezpośrenido
tsGuardSignal = new TTextSound();
tsGuardSignal->Init(NewCommand.c_str(), 30, pVehicle->GetPosition().x,
pVehicle->GetPosition().y, pVehicle->GetPosition().z,
false);
// rsGuardSignal->Stop();
iGuardRadio = 0; // nie przez radio
}
else
{
NewCommand.Insert("radio", NewCommand.Length() - 3); // wstawienie przed kropką
if (FileExists(NewCommand))
{ // wczytanie dźwięku odjazdu w wersji radiowej (słychać tylko w kabinie)
tsGuardSignal = new TTextSound();
tsGuardSignal->Init(NewCommand.c_str(), -1, pVehicle->GetPosition().x,
pVehicle->GetPosition().y, pVehicle->GetPosition().z,
false);
iGuardRadio = iRadioChannel;
}
}
NewCommand = TrainParams->Relation2; // relacja docelowa z rozkładu
}
// jeszcze poustawiać tekstury na wyświetlaczach
TDynamicObject *p = pVehicles[0];
while (p)
{
p->DestinationSet(NewCommand); // relacja docelowa
p = p->Next(); // pojazd podłączony od tyłu (licząc od czoła)
}
}
if (NewLocation) // jeśli podane współrzędne eventu/komórki ustawiającej rozkład (trainset
// nie podaje)
{
vector3 v = *NewLocation - pVehicle->GetPosition(); // wektor do punktu sterującego
vector3 d = pVehicle->VectorFront(); // wektor wskazujący przód
iDirectionOrder = ((v.x * d.x + v.z * d.z) * NewValue1 > 0) ?
1 :
-1; // do przodu, gdy iloczyn skalarny i prędkość dodatnie
/*
if (NewValue1!=0.0) //jeśli ma jechać
if (iDirectionOrder==0) //a kierunek nie był określony (normalnie określany przez
reardriver/headdriver)
iDirectionOrder=NewValue1>0?1:-1; //ustalenie kierunku jazdy względem sprzęgów
else
if (NewValue1<0) //dla ujemnej prędkości
iDirectionOrder=-iDirectionOrder; //ma jechać w drugą stronę
*/
if (AIControllFlag) // jeśli prowadzi komputer
Activation(); // umieszczenie obsługi we właściwym członie, ustawienie nawrotnika w
// przód
}
/*
else
if (!iDirectionOrder)
{//jeśli nie ma kierunku, trzeba ustalić
if (mvOccupied->V!=0.0)
iDirectionOrder=mvOccupied->V>0?1:-1;
else
iDirectionOrder=mvOccupied->ActiveCab;
if (!iDirectionOrder) iDirectionOrder=1;
}
*/
// jeśli wysyłane z Trainset, to wszystko jest już odpowiednio ustawione
// if (!NewLocation) //jeśli wysyłane z Trainset
// if (mvOccupied->CabNo*mvOccupied->V*NewValue1<0) //jeśli zadana prędkość niezgodna z
// aktualnym kierunkiem jazdy
// DirectionForward(false); //jedziemy do tyłu (nawrotnik do tyłu)
// CheckVehicles(); //sprawdzenie składu, AI zapali światła
TableClear(); // wyczyszczenie tabelki prędkości, bo na nowo trzeba określić kierunek i
// sprawdzić przystanki
OrdersInit(
fabs(NewValue1)); // ustalenie tabelki komend wg rozkładu oraz prędkości początkowej
// if (NewValue1!=0.0) if (!AIControllFlag) DirectionForward(NewValue1>0.0); //ustawienie
// nawrotnika użytkownikowi (propaguje się do członów)
// if (NewValue1>0)
// TrainNumber=floor(NewValue1); //i co potem ???
return true; // załatwione
}
if (NewCommand == "SetVelocity")
{
if (NewLocation)
vCommandLocation = *NewLocation;
if ((NewValue1 != 0.0) && (OrderList[OrderPos] != Obey_train))
{ // o ile jazda
if (!iEngineActive)
OrderNext(Prepare_engine); // trzeba odpalić silnik najpierw, światła ustawi
// JumpToNextOrder()
// if (OrderList[OrderPos]!=Obey_train) //jeśli nie pociągowa
OrderNext(Obey_train); // to uruchomić jazdę pociągową (od razu albo po odpaleniu
// silnika
OrderCheck(); // jeśli jazda pociągowa teraz, to wykonać niezbędne operacje
}
if (NewValue1 != 0.0) // jeśli jechać
iDrivigFlags &= ~moveStopHere; // podjeżanie do semaforów zezwolone
else
iDrivigFlags |= moveStopHere; // stać do momentu podania komendy jazdy
SetVelocity(NewValue1, NewValue2, reason); // bylo: nic nie rob bo SetVelocity zewnetrznie
// jest wywolywane przez dynobj.cpp
}
else if (NewCommand == "SetProximityVelocity")
{
/*
if (SetProximityVelocity(NewValue1,NewValue2))
if (NewLocation)
vCommandLocation=*NewLocation;
*/
}
else if (NewCommand == "ShuntVelocity")
{ // uruchomienie jazdy manewrowej bądź zmiana prędkości
if (NewLocation)
vCommandLocation = *NewLocation;
// if (OrderList[OrderPos]=Obey_train) and (NewValue1<>0))
if (!iEngineActive)
OrderNext(Prepare_engine); // trzeba odpalić silnik najpierw
OrderNext(Shunt); // zamieniamy w aktualnej pozycji, albo dodajey za odpaleniem silnika
if (NewValue1 != 0.0)
{
// if (iVehicleCount>=0) WriteLog("Skasowano ilosć wagonów w ShuntVelocity!");
iVehicleCount = -2; // wartość neutralna
CheckVehicles(); // zabrać to do OrderCheck()
}
// dla prędkości ujemnej przestawić nawrotnik do tyłu? ale -1=brak ograniczenia !!!!
// if (iDrivigFlags&movePress) WriteLog("Skasowano docisk w ShuntVelocity!");
iDrivigFlags &= ~movePress; // bez dociskania
SetVelocity(NewValue1, NewValue2, reason);
if (NewValue1 != 0.0)
iDrivigFlags &= ~moveStopHere; // podjeżanie do semaforów zezwolone
else
iDrivigFlags |= moveStopHere; // ma stać w miejscu
if (fabs(NewValue1) > 2.0) // o ile wartość jest sensowna (-1 nie jest konkretną wartością)
fShuntVelocity = fabs(NewValue1); // zapamiętanie obowiązującej prędkości dla manewrów
}
else if (NewCommand == "Wait_for_orders")
{ // oczekiwanie; NewValue1 - czas oczekiwania, -1 = na inną komendę
if (NewValue1 > 0.0 ? NewValue1 > fStopTime : false)
fStopTime = NewValue1; // Ra: włączenie czekania bez zmiany komendy
else
OrderList[OrderPos] = Wait_for_orders; // czekanie na komendę (albo dać OrderPos=0)
}
else if (NewCommand == "Prepare_engine")
{ // włączenie albo wyłączenie silnika (w szerokim sensie)
OrdersClear(); // czyszczenie tabelki rozkazów, aby nic dalej nie robił
if (NewValue1 == 0.0)
OrderNext(Release_engine); // wyłączyć silnik (przygotować pojazd do jazdy)
else if (NewValue1 > 0.0)
OrderNext(Prepare_engine); // odpalić silnik (wyłączyć wszystko, co się da)
// po załączeniu przejdzie do kolejnej komendy, po wyłączeniu na Wait_for_orders
}
else if (NewCommand == "Change_direction")
{
TOrders o = OrderList[OrderPos]; // co robił przed zmianą kierunku
if (!iEngineActive)
OrderNext(Prepare_engine); // trzeba odpalić silnik najpierw
if (NewValue1 == 0.0)
iDirectionOrder = -iDirection; // zmiana na przeciwny niż obecny
else if (NewLocation) // jeśli podane współrzędne eventu/komórki ustawiającej rozkład
// (trainset nie podaje)
{
vector3 v = *NewLocation - pVehicle->GetPosition(); // wektor do punktu sterującego
vector3 d = pVehicle->VectorFront(); // wektor wskazujący przód
iDirectionOrder = ((v.x * d.x + v.z * d.z) * NewValue1 > 0) ?
1 :
-1; // do przodu, gdy iloczyn skalarny i prędkość dodatnie
// iDirectionOrder=1; else if (NewValue1<0.0) iDirectionOrder=-1;
}
if (iDirectionOrder != iDirection)
OrderNext(Change_direction); // zadanie komendy do wykonania
if (o >= Shunt) // jeśli jazda manewrowa albo pociągowa
OrderNext(o); // to samo robić po zmianie
else if (!o) // jeśli wcześniej było czekanie
OrderNext(Shunt); // to dalej jazda manewrowa
if (mvOccupied->Vel >= 1.0) // jeśli jedzie
iDrivigFlags &= ~moveStartHorn; // to bez trąbienia po ruszeniu z zatrzymania
// Change_direction wykona się samo i następnie przejdzie do kolejnej komendy
}
else if (NewCommand == "Obey_train")
{
if (!iEngineActive)
OrderNext(Prepare_engine); // trzeba odpalić silnik najpierw
OrderNext(Obey_train);
// if (NewValue1>0) TrainNumber=floor(NewValue1); //i co potem ???
OrderCheck(); // jeśli jazda pociągowa teraz, to wykonać niezbędne operacje
}
else if (NewCommand == "Shunt")
{ // NewValue1 - ilość wagonów (-1=wszystkie); NewValue2: 0=odczep, 1..63=dołącz, -1=bez zmian
//-3,-y - podłączyć do całego stojącego składu (sprzęgiem y>=1), zmienić kierunek i czekać w
// trybie pociągowym
//-2,-y - podłączyć do całego stojącego składu (sprzęgiem y>=1), zmienić kierunek i czekać
//-2, y - podłączyć do całego stojącego składu (sprzęgiem y>=1) i czekać
//-1,-y - podłączyć do całego stojącego składu (sprzęgiem y>=1) i jechać w powrotną stronę
//-1, y - podłączyć do całego stojącego składu (sprzęgiem y>=1) i jechać dalej
//-1, 0 - tryb manewrowy bez zmian (odczepianie z pozostawieniem wagonów nie ma sensu)
// 0, 0 - odczepienie lokomotywy
// 1,-y - podłączyć się do składu (sprzęgiem y>=1), a następnie odczepić i zabrać (x)
// wagonów
// 1, 0 - odczepienie lokomotywy z jednym wagonem
iDrivigFlags &= ~moveStopHere; // podjeżanie do semaforów zezwolone
if (!iEngineActive)
OrderNext(Prepare_engine); // trzeba odpalić silnik najpierw
if (NewValue2 != 0) // jeśli podany jest sprzęg
{
iCoupler = floor(fabs(NewValue2)); // jakim sprzęgiem
OrderNext(Connect); // najpierw połącz pojazdy
if (NewValue1 >= 0.0) // jeśli ilość wagonów inna niż wszystkie
{ // to po podpięciu należy się odczepić
iDirectionOrder = -iDirection; // zmiana na ciągnięcie
OrderPush(Change_direction); // najpierw zmień kierunek, bo odczepiamy z tyłu
OrderPush(Disconnect); // a odczep już po zmianie kierunku
}
else if (NewValue2 < 0.0) // jeśli wszystkie, a sprzęg ujemny, to tylko zmiana kierunku
// po podczepieniu
{ // np. Shunt -1 -3
iDirectionOrder = -iDirection; // jak się podczepi, to jazda w przeciwną stronę
OrderNext(Change_direction);
}
WaitingTime =
0.0; // nie ma co dalej czekać, można zatrąbić i jechać, chyba że już jedzie
}
else // if (NewValue2==0.0) //zerowy sprzęg
if (NewValue1 >= 0.0) // jeśli ilość wagonów inna niż wszystkie
{ // będzie odczepianie, ale jeśli wagony są z przodu, to trzeba najpierw zmienić kierunek
if ((mvOccupied->Couplers[mvOccupied->DirAbsolute > 0 ? 1 : 0].CouplingFlag ==
0) ? // z tyłu nic
(mvOccupied->Couplers[mvOccupied->DirAbsolute > 0 ? 0 : 1].CouplingFlag > 0) :
false) // a z przodu skład
{
iDirectionOrder = -iDirection; // zmiana na ciągnięcie
OrderNext(Change_direction); // najpierw zmień kierunek (zastąpi Disconnect)
OrderPush(Disconnect); // a odczep już po zmianie kierunku
}
else if (mvOccupied->Couplers[mvOccupied->DirAbsolute > 0 ? 1 : 0].CouplingFlag >
0) // z tyłu coś
OrderNext(Disconnect); // jak ciągnie, to tylko odczep (NewValue1) wagonów
WaitingTime =
0.0; // nie ma co dalej czekać, można zatrąbić i jechać, chyba że już jedzie
}
if (NewValue1 == -1.0)
{
iDrivigFlags &= ~moveStopHere; // ma jechać
WaitingTime = 0.0; // nie ma co dalej czekać, można zatrąbić i jechać
}
if (NewValue1 < -1.5) // jeśli -2/-3, czyli czekanie z ruszeniem na sygnał
iDrivigFlags |= moveStopHere; // nie podjeżdżać do semafora, jeśli droga nie jest wolna
// (nie dotyczy Connect)
if (NewValue1 < -2.5) // jeśli
OrderNext(Obey_train); // to potem jazda pociągowa
else
OrderNext(Shunt); // to potem manewruj dalej
CheckVehicles(); // sprawdzić światła
// if ((iVehicleCount>=0)&&(NewValue1<0)) WriteLog("Skasowano ilosć wagonów w Shunt!");
if (NewValue1 != iVehicleCount)
iVehicleCount = floor(NewValue1); // i co potem ? - trzeba zaprogramowac odczepianie
/*
if (NewValue1!=-1.0)
if (NewValue2!=0.0)
if (VelDesired==0)
SetVelocity(20,0); //to niech jedzie
*/
}
else if (NewCommand == "Jump_to_first_order")
JumpToFirstOrder();
else if (NewCommand == "Jump_to_order")
{
if (NewValue1 == -1.0)
JumpToNextOrder();
else if ((NewValue1 >= 0) && (NewValue1 < maxorders))
{
OrderPos = floor(NewValue1);
if (!OrderPos)
OrderPos = 1; // zgodność wstecz: dopiero pierwsza uruchamia
#if LOGORDERS
WriteLog("--> Jump_to_order");
OrdersDump();
#endif
}
/*
if (WriteLogFlag)
{
append(AIlogFile);
writeln(AILogFile,ElapsedTime:5:2," - new order: ",Order2Str( OrderList[OrderPos])," @
",OrderPos);
close(AILogFile);
}
*/
}
/* //ta komenda jest teraz skanowana, więc wysyłanie jej eventem nie ma sensu
else if (NewCommand=="OutsideStation") //wskaznik W5
{
if (OrderList[OrderPos]==Obey_train)
SetVelocity(NewValue1,NewValue2,stopOut); //koniec stacji - predkosc szlakowa
else //manewry - zawracaj
{
iDirectionOrder=-iDirection; //zmiana na przeciwny niż obecny
OrderNext(Change_direction); //zmiana kierunku
OrderNext(Shunt); //a dalej manewry
iDrivigFlags&=~moveStartHorn; //bez trąbienia po zatrzymaniu
}
}
*/
else if (NewCommand == "Warning_signal")
{
if (AIControllFlag) // poniższa komenda nie jest wykonywana przez użytkownika
if (NewValue1 > 0)
{
fWarningDuration = NewValue1; // czas trąbienia
mvOccupied->WarningSignal = (NewValue2 > 1) ? 2 : 1; // wysokość tonu
}
}
else if (NewCommand == "Radio_channel")
{ // wybór kanału radiowego (którego powinien używać AI, ręczny maszynista musi go ustawić sam)
if (NewValue1 >= 0) // wartości ujemne są zarezerwowane, -1 = nie zmieniać kanału
{
iRadioChannel = NewValue1;
if (iGuardRadio)
iGuardRadio = iRadioChannel; // kierownikowi też zmienić
}
// NewValue2 może zawierać dodatkowo oczekiwany kod odpowiedzi, np. dla W29 "nawiązać
// łączność radiową z dyżurnym ruchu odcinkowym"
}
else
return false; // nierozpoznana - wysłać bezpośrednio do pojazdu
return true; // komenda została przetworzona
};
void TController::PhysicsLog()
{ // zapis logu - na razie tylko wypisanie parametrów
if (LogFile.is_open())
{
#if LOGPRESS == 0
LogFile << ElapsedTime << " " << fabs(11.31 * mvOccupied->WheelDiameter * mvOccupied->nrot)
<< " ";
LogFile << mvControlling->AccS << " " << mvOccupied->Couplers[1].Dist << " "
<< mvOccupied->Couplers[1].CForce << " ";
LogFile << mvOccupied->Ft << " " << mvOccupied->Ff << " " << mvOccupied->Fb << " "
<< mvOccupied->BrakePress << " ";
LogFile << mvOccupied->PipePress << " " << mvControlling->Im << " "
<< int(mvControlling->MainCtrlPos) << " ";
LogFile << int(mvControlling->ScndCtrlPos) << " " << int(mvOccupied->BrakeCtrlPos)
<< " " << int(mvOccupied->LocalBrakePos) << " ";
LogFile << int(mvControlling->ActiveDir) << " " << mvOccupied->CommandIn.Command.c_str()
<< " " << mvOccupied->CommandIn.Value1 << " ";
LogFile << mvOccupied->CommandIn.Value2 << " " << int(mvControlling->SecuritySystem.Status)
<< " " << int(mvControlling->SlippingWheels) << "\r\n";
#endif
#if LOGPRESS == 1
LogFile << ElapsedTime << "\t" << fabs(11.31 * mvOccupied->WheelDiameter * mvOccupied->nrot)
<< "\t";
LogFile << Controlling->AccS << "\t";
LogFile << mvOccupied->PipePress << "\t" << mvOccupied->CntrlPipePress << "\t"
<< mvOccupied->BrakePress << "\t";
LogFile << mvOccupied->Volume << "\t" << mvOccupied->Hamulec->GetCRP() << "\n";
#endif
LogFile.flush();
}
};
bool TController::UpdateSituation(double dt)
{ // uruchamiać przynajmniej raz na sekundę
if ((iDrivigFlags & movePrimary) == 0)
return true; // pasywny nic nie robi
double AbsAccS;
// double VelReduced; //o ile km/h może przekroczyć dozwoloną prędkość bez hamowania
bool UpdateOK = false;
if (AIControllFlag)
{ // yb: zeby EP nie musial sie bawic z ciesnieniem w PG
// if (mvOccupied->BrakeSystem==ElectroPneumatic)
// mvOccupied->PipePress=0.5; //yB: w SPKS są poprawnie zrobione pozycje
if (mvControlling->SlippingWheels)
{
mvControlling->SandDoseOn(); // piasku!
// Controlling->SlippingWheels=false; //a to tu nie ma sensu, flaga używana w dalszej
// części
}
}
// ABu-160305 testowanie gotowości do jazdy
// Ra: przeniesione z DynObj, skład użytkownika też jest testowany, żeby mu przekazać, że ma
// odhamować
Ready = true; // wstępnie gotowy
fReady = 0.0; // założenie, że odhamowany
fAccGravity = 0.0; // przyspieszenie wynikające z pochylenia
double dy; // składowa styczna grawitacji, w przedziale <0,1>
TDynamicObject *p = pVehicles[0]; // pojazd na czole składu
while (p)
{ // sprawdzenie odhamowania wszystkich połączonych pojazdów
if (Ready) // bo jak coś nie odhamowane, to dalej nie ma co sprawdzać
// if (p->MoverParameters->BrakePress>=0.03*p->MoverParameters->MaxBrakePress)
if (p->MoverParameters->BrakePress >= 0.4) // wg UIC określone sztywno na 0.04
{
Ready = false; // nie gotowy
// Ra: odluźnianie przeładowanych lokomotyw, ciągniętych na zimno - prowizorka...
if (AIControllFlag) // skład jak dotąd był wyluzowany
{
if (mvOccupied->BrakeCtrlPos == 0) // jest pozycja jazdy
if ((p->MoverParameters->PipePress - 5.0) >
-0.1) // jeśli ciśnienie jak dla jazdy
if (p->MoverParameters->Hamulec->GetCRP() >
p->MoverParameters->PipePress + 0.12) // za dużo w zbiorniku
p->MoverParameters->BrakeReleaser(1); // indywidualne luzowanko
if (p->MoverParameters->Power > 0.01) // jeśli ma silnik
if (p->MoverParameters->FuseFlag) // wywalony nadmiarowy
Need_TryAgain = true; // reset jak przy wywaleniu nadmiarowego
}
}
if (fReady < p->MoverParameters->BrakePress)
fReady = p->MoverParameters->BrakePress; // szukanie najbardziej zahamowanego
if ((dy = p->VectorFront().y) != 0.0) // istotne tylko dla pojazdów na pochyleniu
fAccGravity -= p->DirectionGet() * p->MoverParameters->TotalMassxg *
dy; // ciężar razy składowa styczna grawitacji
p = p->Next(); // pojazd podłączony z tyłu (patrząc od czoła)
}
if (iDirection)
fAccGravity /=
iDirection *
fMass; // siłę generują pojazdy na pochyleniu ale działa ona całość składu, więc a=F/m
if (!Ready) // v367: jeśli wg powyższych warunków skład nie jest odhamowany
if (fAccGravity < -0.05) // jeśli ma pod górę na tyle, by się stoczyć
// if (mvOccupied->BrakePress<0.08) //to wystarczy, że zadziałają liniowe (nie ma ich
// jeszcze!!!)
if (fReady < 0.8) // delikatniejszy warunek, obejmuje wszystkie wagony
Ready = true; //żeby uznać za odhamowany
HelpMeFlag = false;
// Winger 020304
if (AIControllFlag)
{
if (mvControlling->EnginePowerSource.SourceType == CurrentCollector)
{
if (mvOccupied->ScndPipePress > 4.3) // gdy główna sprężarka bezpiecznie nabije
// ciśnienie
mvControlling->bPantKurek3 =
true; // to można przestawić kurek na zasilanie pantografów z głównej pneumatyki
fVoltage =
0.5 * (fVoltage +
fabs(mvControlling->RunningTraction.TractionVoltage)); // uśrednione napięcie
// sieci: przy spadku
// poniżej wartości
// minimalnej opóźnić
// rozruch o losowy
// czas
if (fVoltage < mvControlling->EnginePowerSource.CollectorParameters
.MinV) // gdy rozłączenie WS z powodu niskiego napięcia
if (fActionTime >= 0) // jeśli czas oczekiwania nie został ustawiony
fActionTime =
-2 - random(10); // losowy czas oczekiwania przed ponownym załączeniem jazdy
}
if (mvOccupied->Vel > 0.0)
{ // jeżeli jedzie
if (iDrivigFlags & moveDoorOpened) // jeśli drzwi otwarte
if (mvOccupied->Vel > 1.0) // nie zamykać drzwi przy drganiach, bo zatrzymanie na W4
// akceptuje niewielkie prędkości
Doors(false);
// przy prowadzeniu samochodu trzeba każdą oś odsuwać oddzielnie, inaczej kicha wychodzi
if (mvOccupied->CategoryFlag & 2) // jeśli samochód
// if (fabs(mvOccupied->OffsetTrackH)<mvOccupied->Dim.W) //Ra: szerokość drogi tu
// powinna być?
if (!mvOccupied->ChangeOffsetH(-0.01 * mvOccupied->Vel * dt)) // ruch w poprzek
// drogi
mvOccupied->ChangeOffsetH(0.01 * mvOccupied->Vel *
dt); // Ra: co to miało być, to nie wiem
if (mvControlling->EnginePowerSource.SourceType == CurrentCollector)
{
if ((fOverhead2 >= 0.0) || iOverheadZero)
{ // jeśli jazda bezprądowa albo z opuszczonym pantografem
while (DecSpeed(true))
; // zerowanie napędu
}
if ((fOverhead2 > 0.0) || iOverheadDown)
{ // jazda z opuszczonymi pantografami
mvControlling->PantFront(false);
mvControlling->PantRear(false);
}
else
{ // jeśli nie trzeba opuszczać pantografów
if (iDirection >= 0) // jak jedzie w kierunku sprzęgu 0
mvControlling->PantRear(true); // jazda na tylnym
else
mvControlling->PantFront(true);
}
if (mvOccupied->Vel > 10) // opuszczenie przedniego po rozpędzeniu się
{
if (mvControlling->EnginePowerSource.CollectorParameters.CollectorsNo >
1) // o ile jest więcej niż jeden
if (iDirection >= 0) // jak jedzie w kierunku sprzęgu 0
{ // poczekać na podniesienie tylnego
if (mvControlling->PantRearVolt !=
0.0) // czy jest napięcie zasilające na tylnym?
mvControlling->PantFront(false); // opuszcza od sprzęgu 0
}
else
{ // poczekać na podniesienie przedniego
if (mvControlling->PantFrontVolt !=
0.0) // czy jest napięcie zasilające na przednim?
mvControlling->PantRear(false); // opuszcza od sprzęgu 1
}
}
}
}
if (iDrivigFlags & moveStartHornNow) // czy ma zatrąbić przed ruszeniem?
if (Ready) // gotów do jazdy
if (iEngineActive) // jeszcze się odpalić musi
if (fStopTime >= 0) // i nie musi czekać
{ // uruchomienie trąbienia
fWarningDuration = 0.3; // czas trąbienia
// if (AIControllFlag) //jak siedzi krasnoludek, to włączy trąbienie
mvOccupied->WarningSignal =
pVehicle->iHornWarning; // wysokość tonu (2=wysoki)
iDrivigFlags |= moveStartHornDone; // nie trąbić aż do ruszenia
iDrivigFlags &= ~moveStartHornNow; // trąbienie zostało zorganizowane
}
}
ElapsedTime += dt;
WaitingTime += dt;
fBrakeTime -=
dt; // wpisana wartość jest zmniejszana do 0, gdy ujemna należy zmienić nastawę hamulca
fStopTime += dt; // zliczanie czasu postoju, nie ruszy dopóki ujemne
fActionTime += dt; // czas używany przy regulacji prędkości i zamykaniu drzwi
if (WriteLogFlag)
{
if (LastUpdatedTime > deltalog)
{ // zapis do pliku DAT
PhysicsLog();
if (fabs(mvOccupied->V) > 0.1) // Ra: [m/s]
deltalog = 0.05; // 0.2;
else
deltalog = 0.05; // 1.0;
LastUpdatedTime = 0.0;
}
else
LastUpdatedTime = LastUpdatedTime + dt;
}
// Ra: skanowanie również dla prowadzonego ręcznie, aby podpowiedzieć prędkość
if ((LastReactionTime > Min0R(ReactionTime, 2.0)))
{
// Ra: nie wiem czemu ReactionTime potrafi dostać 12 sekund, to jest przegięcie, bo przeżyna
// STÓJ
// yB: otóż jest to jedna trzecia czasu napełniania na towarowym; może się przydać przy
// wdrażaniu hamowania, żeby nie ruszało kranem jak głupie
// Ra: ale nie może się budzić co pół minuty, bo przeżyna semafory
// Ra: trzeba by tak:
// 1. Ustalić istotną odległość zainteresowania (np. 3×droga hamowania z V.max).
fBrakeDist = fDriverBraking * mvOccupied->Vel *
(40.0 + mvOccupied->Vel); // przybliżona droga hamowania
// dla hamowania -0.2 [m/ss] droga wynosi 0.389*Vel*Vel [km/h], czyli 600m dla 40km/h, 3.8km
// dla 100km/h i 9.8km dla 160km/h
// dla hamowania -0.4 [m/ss] droga wynosi 0.096*Vel*Vel [km/h], czyli 150m dla 40km/h, 1.0km
// dla 100km/h i 2.5km dla 160km/h
// ogólnie droga hamowania przy stałym opóźnieniu to Vel*Vel/(3.6*3.6*a) [m]
// fBrakeDist powinno być wyznaczane dla danego składu za pomocą sieci neuronowych, w
// zależności od prędkości i siły (ciśnienia) hamowania
// następnie w drugą stronę, z drogi hamowania i chwilowej prędkości powinno być wyznaczane
// zalecane ciśnienie
if (fMass > 1000000.0)
fBrakeDist *= 2.0; // korekta dla ciężkich, bo przeżynają - da to coś?
if (mvOccupied->BrakeDelayFlag == bdelay_G)
fBrakeDist = fBrakeDist + 2 * mvOccupied->Vel; // dla nastawienia G
// koniecznie należy wydłużyć drogę na czas reakcji
// double scanmax=(mvOccupied->Vel>0.0)?3*fDriverDist+fBrakeDist:10.0*fDriverDist;
double scanmax = (mvOccupied->Vel > 5.0) ?
400 + fBrakeDist :
30.0 * fDriverDist; // 1500m dla stojących pociągów; Ra 2015-01: przy
// dłuższej drodze skanowania AI jeździ spokojniej
// 2. Sprawdzić, czy tabelka pokrywa założony odcinek (nie musi, jeśli jest STOP).
// 3. Sprawdzić, czy trajektoria ruchu przechodzi przez zwrotnice - jeśli tak, to sprawdzić,
// czy stan się nie zmienił.
// 4. Ewentualnie uzupełnić tabelkę informacjami o sygnałach i ograniczeniach, jeśli się
// "zużyła".
TableCheck(scanmax); // wypełnianie tabelki i aktualizacja odległości
// 5. Sprawdzić stany sygnalizacji zapisanej w tabelce, wyznaczyć prędkości.
// 6. Z tabelki wyznaczyć krytyczną odległość i prędkość (najmniejsze przyspieszenie).
// 7. Jeśli jest inny pojazd z przodu, ewentualnie skorygować odległość i prędkość.
// 8. Ustalić częstotliwość świadomości AI (zatrzymanie precyzyjne - częściej, brak atrakcji
// - rzadziej).
if (AIControllFlag)
{ // tu bedzie logika sterowania
if (mvOccupied->CommandIn.Command != "")
if (!mvOccupied->RunInternalCommand()) // rozpoznaj komende bo lokomotywa jej nie
// rozpoznaje
RecognizeCommand(); // samo czyta komendę wstawioną do pojazdu?
if (mvOccupied->SecuritySystem.Status > 1) // jak zadziałało CA/SHP
if (!mvOccupied->SecuritySystemReset()) // to skasuj
// if
// ((TestFlag(mvOccupied->SecuritySystem.Status,s_ebrake))&&(mvOccupied->BrakeCtrlPos==0)&&(AccDesired>0.0))
if ((TestFlag(mvOccupied->SecuritySystem.Status, s_SHPebrake) ||
TestFlag(mvOccupied->SecuritySystem.Status, s_CAebrake)) &&
(mvOccupied->BrakeCtrlPos == 0) && (AccDesired > 0.0))
mvOccupied->BrakeLevelSet(
0); //!!! hm, może po prostu normalnie sterować hamulcem?
}
switch (OrderList[OrderPos])
{ // ustalenie prędkości przy doczepianiu i odczepianiu, dystansów w pozostałych przypadkach
case Connect: // podłączanie do składu
if (iDrivigFlags & moveConnect)
{ // jeśli stanął już blisko, unikając zderzenia i można próbować podłączyć
fMinProximityDist = -0.01;
fMaxProximityDist = 0.0; //[m] dojechać maksymalnie
fVelPlus = 0.5; // dopuszczalne przekroczenie prędkości na ograniczeniu bez
// hamowania
fVelMinus = 0.5; // margines prędkości powodujący załączenie napędu
if (AIControllFlag)
{ // to robi tylko AI, wersję dla człowieka trzeba dopiero zrobić
// sprzęgi sprawdzamy w pierwszej kolejności, bo jak połączony, to koniec
bool ok; // true gdy się podłączy (uzyskany sprzęg będzie zgodny z żądanym)
if (pVehicles[0]->DirectionGet() > 0) // jeśli sprzęg 0
{ // sprzęg 0 - próba podczepienia
if (pVehicles[0]->MoverParameters->Couplers[0].Connected) // jeśli jest coś
// wykryte (a
// chyba jest,
// nie?)
if (pVehicles[0]->MoverParameters->Attach(
0, 2, pVehicles[0]->MoverParameters->Couplers[0].Connected,
iCoupler))
{
// pVehicles[0]->dsbCouplerAttach->SetVolume(DSBVOLUME_MAX);
// pVehicles[0]->dsbCouplerAttach->Play(0,0,0);
}
// WriteLog("CoupleDist[0]="+AnsiString(pVehicles[0]->MoverParameters->Couplers[0].CoupleDist)+",
// Connected[0]="+AnsiString(pVehicles[0]->MoverParameters->Couplers[0].CouplingFlag));
ok = (pVehicles[0]->MoverParameters->Couplers[0].CouplingFlag ==
iCoupler); // udało się? (mogło częściowo)
}
else // if (pVehicles[0]->MoverParameters->DirAbsolute<0) //jeśli sprzęg 1
{ // sprzęg 1 - próba podczepienia
if (pVehicles[0]->MoverParameters->Couplers[1].Connected) // jeśli jest coś
// wykryte (a
// chyba jest,
// nie?)
if (pVehicles[0]->MoverParameters->Attach(
1, 2, pVehicles[0]->MoverParameters->Couplers[1].Connected,
iCoupler))
{
// pVehicles[0]->dsbCouplerAttach->SetVolume(DSBVOLUME_MAX);
// pVehicles[0]->dsbCouplerAttach->Play(0,0,0);
}
// WriteLog("CoupleDist[1]="+AnsiString(Controlling->Couplers[1].CoupleDist)+",
// Connected[0]="+AnsiString(Controlling->Couplers[1].CouplingFlag));
ok = (pVehicles[0]->MoverParameters->Couplers[1].CouplingFlag ==
iCoupler); // udało się? (mogło częściowo)
}
if (ok)
{ // jeżeli został podłączony
iCoupler = 0; // dalsza jazda manewrowa już bez łączenia
iDrivigFlags &= ~moveConnect; // zdjęcie flagi doczepiania
SetVelocity(0, 0, stopJoin); // wyłączyć przyspieszanie
CheckVehicles(); // sprawdzić światła nowego składu
JumpToNextOrder(); // wykonanie następnej komendy
}
else
SetVelocity(2.0, 0.0); // jazda w ustawionym kierunku z prędkością 2 (18s)
} // if (AIControllFlag) //koniec zblokowania, bo była zmienna lokalna
/* //Ra 2014-02: lepiej tam, bo jak tam się odźwieży skład, to tu pVehicles[0]
będzie czymś innym
else
{//jeśli człowiek ma podłączyć, to czekamy na zmianę stanu sprzęgów na końcach
dotychczasowego składu
bool ok; //true gdy się podłączy (uzyskany sprzęg będzie zgodny z żądanym)
if (pVehicles[0]->DirectionGet()>0) //jeśli sprzęg 0
ok=(pVehicles[0]->MoverParameters->Couplers[0].CouplingFlag>0);
//==iCoupler); //udało się? (mogło częściowo)
else //if (pVehicles[0]->MoverParameters->DirAbsolute<0) //jeśli sprzęg 1
ok=(pVehicles[0]->MoverParameters->Couplers[1].CouplingFlag>0);
//==iCoupler); //udało się? (mogło częściowo)
if (ok)
{//jeżeli został podłączony
iDrivigFlags&=~moveConnect; //zdjęcie flagi doczepiania
JumpToNextOrder(); //wykonanie następnej komendy
}
}
*/
}
else
{ // jak daleko, to jazda jak dla Shunt na kolizję
fMinProximityDist = 0.0;
fMaxProximityDist = 5.0; //[m] w takim przedziale odległości powinien stanąć
fVelPlus = 2.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez
// hamowania
fVelMinus = 1.0; // margines prędkości powodujący załączenie napędu
// VelReduced=5; //[km/h]
// if (mvOccupied->Vel<0.5) //jeśli już prawie stanął
if (pVehicles[0]->fTrackBlock <=
20.0) // przy zderzeniu fTrackBlock nie jest miarodajne
iDrivigFlags |=
moveConnect; // początek podczepiania, z wyłączeniem sprawdzania fTrackBlock
}
break;
case Disconnect: // 20.07.03 - manewrowanie wagonami
fMinProximityDist = 1.0;
fMaxProximityDist = 10.0; //[m]
fVelPlus = 1.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania
fVelMinus = 0.5; // margines prędkości powodujący załączenie napędu
if (AIControllFlag)
{
if (iVehicleCount >= 0) // jeśli była podana ilość wagonów
{
if (iDrivigFlags & movePress) // jeśli dociskanie w celu odczepienia
{ // 3. faza odczepiania.
SetVelocity(2, 0); // jazda w ustawionym kierunku z prędkością 2
if ((mvControlling->MainCtrlPos > 0) ||
(mvOccupied->BrakeSystem == ElectroPneumatic)) // jeśli jazda
{
// WriteLog("Odczepianie w kierunku
// "+AnsiString(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 ?
0 :
1; // 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ć
p->MoverParameters->BrakeLevelSet(
0); // hamulec na zero, aby nie hamował
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 ? p->MoverParameters->Couplers[d].CouplingFlag == 0 : true)
iVehicleCount = -2; // odczepiono, co było do odczepienia
else if (!p->Dettach(d)) // zwraca maskę bitową połączenia; usuwa
// własność pojazdów
{ // tylko jeśli odepnie
// WriteLog("Odczepiony od strony ");
iVehicleCount = -2;
} // a jak nie, to dociskać dalej
}
if (iVehicleCount >= 0) // zmieni się po odczepieniu
if (!mvOccupied->DecLocalBrakeLevel(1))
{ // dociśnij sklad
// WriteLog("Dociskanie");
// mvOccupied->BrakeReleaser(); //wyluzuj lokomotywę
// Ready=true; //zamiast sprawdzenia odhamowania całego składu
IncSpeed(); // dla (Ready)==false nie ruszy
}
}
if ((mvOccupied->Vel == 0.0) && !(iDrivigFlags & movePress))
{ // 2. faza odczepiania: zmień kierunek na przeciwny i dociśnij
// za radą yB ustawiamy pozycję 3 kranu (ruszanie kranem w innych miejscach
// powino zostać wyłączone)
// WriteLog("Zahamowanie składu");
// while ((mvOccupied->BrakeCtrlPos>3)&&mvOccupied->DecBrakeLevel());
// while ((mvOccupied->BrakeCtrlPos<3)&&mvOccupied->IncBrakeLevel());
mvOccupied->BrakeLevelSet(mvOccupied->BrakeSystem == ElectroPneumatic ? 1 :
3);
double p = mvOccupied->BrakePressureActual
.PipePressureVal; // tu może być 0 albo -1 nawet
if (p < 3.9)
p = 3.9; // TODO: zabezpieczenie przed dziwnymi CHK do czasu wyjaśnienia
// sensu 0 oraz -1 w tym miejscu
if (mvOccupied->BrakeSystem == ElectroPneumatic ?
mvOccupied->BrakePress > 2 :
mvOccupied->PipePress < p + 0.1)
{ // jeśli w miarę został zahamowany (ciśnienie mniejsze niż podane na
// pozycji 3, zwyle 0.37)
if (mvOccupied->BrakeSystem == ElectroPneumatic)
mvOccupied->BrakeLevelSet(0); // wyłączenie EP, gdy wystarczy (może
// nie być potrzebne, bo na początku
// jest)
// WriteLog("Luzowanie lokomotywy i zmiana kierunku");
mvOccupied->BrakeReleaser(1); // wyluzuj lokomotywę; a ST45?
mvOccupied->DecLocalBrakeLevel(10); // zwolnienie hamulca
iDrivigFlags |= movePress; // następnie będzie dociskanie
DirectionForward(mvOccupied->ActiveDir <
0); // zmiana kierunku jazdy na przeciwny (dociskanie)
CheckVehicles(); // od razu zmienić światła (zgasić) - bez tego się nie
// odczepi
fStopTime = 0.0; // nie ma na co czekać z odczepianiem
}
}
} // odczepiania
else // to poniżej jeśli ilość wagonów ujemna
if (iDrivigFlags & movePress)
{ // 4. faza odczepiania: zwolnij i zmień kierunek
SetVelocity(0, 0, stopJoin); // wyłączyć przyspieszanie
if (!DecSpeed()) // jeśli już bardziej wyłączyć się nie da
{ // ponowna zmiana kierunku
// WriteLog("Ponowna zmiana kierunku");
DirectionForward(mvOccupied->ActiveDir <
0); // zmiana kierunku jazdy na właściwy
iDrivigFlags &= ~movePress; // koniec dociskania
JumpToNextOrder(); // zmieni światła
TableClear(); // skanowanie od nowa
iDrivigFlags &= ~moveStartHorn; // bez trąbienia przed ruszeniem
SetVelocity(fShuntVelocity, fShuntVelocity); // ustawienie prędkości jazdy
}
}
}
break;
case Shunt:
// na jaką odleglość i z jaką predkością ma podjechać
fMinProximityDist = 2.0;
fMaxProximityDist = 4.0; //[m]
fVelPlus = 2.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania
fVelMinus = 3.0; // margines prędkości powodujący załączenie napędu
if (fVelMinus > 0.1 * fShuntVelocity)
fVelMinus =
0.1 *
fShuntVelocity; // były problemy z jazdą np. 3km/h podczas ładowania wagonów
break;
case Obey_train:
// na jaka odleglosc i z jaka predkoscia ma podjechac do przeszkody
if (mvOccupied->CategoryFlag & 1) // jeśli pociąg
{
fMinProximityDist = 10.0;
fMaxProximityDist =
(mvOccupied->Vel > 0.0) ?
20.0 :
50.0; //[m] jak stanie za daleko, to niech nie dociąga paru metrów
if (iDrivigFlags & moveLate)
{
fVelMinus = 1.0; // jeśli spóźniony, to gna
fVelPlus =
5.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania
}
else
{ // gdy nie musi się sprężać
fVelMinus =
int(0.05 * VelDesired); // margines prędkości powodujący załączenie napędu
if (fVelMinus > 5.0)
fVelMinus = 5.0;
else if (fVelMinus < 1.0)
fVelMinus = 1.0; //żeby nie ruszał przy 0.1
fVelPlus = int(
0.5 +
0.05 * VelDesired); // normalnie dopuszczalne przekroczenie to 5% prędkości
if (fVelPlus > 5.0)
fVelPlus = 5.0; // ale nie więcej niż 5km/h
}
}
else // samochod (sokista też)
{
fMinProximityDist = 7.0;
fMaxProximityDist = 10.0; //[m]
fVelPlus =
10.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania
fVelMinus = 2.0; // margines prędkości powodujący załączenie napędu
}
// VelReduced=4; //[km/h]
break;
default:
fMinProximityDist = 0.01;
fMaxProximityDist = 2.0; //[m]
fVelPlus = 2.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania
fVelMinus = 5.0; // margines prędkości powodujący załączenie napędu
} // switch
switch (OrderList[OrderPos])
{ // co robi maszynista
case Prepare_engine: // odpala silnik
// if (AIControllFlag)
if (PrepareEngine()) // dla użytkownika tylko sprawdza, czy uruchomił
{ // gotowy do drogi?
SetDriverPsyche();
// OrderList[OrderPos]:=Shunt; //Ra: to nie może tak być, bo scenerie robią
// Jump_to_first_order i przechodzi w manewrowy
JumpToNextOrder(); // w następnym jest Shunt albo Obey_train, moze też być
// Change_direction, Connect albo Disconnect
// if OrderList[OrderPos]<>Wait_for_Orders)
// if BrakeSystem=Pneumatic) //napelnianie uderzeniowe na wstepie
// if BrakeSubsystem=Oerlikon)
// if (BrakeCtrlPos=0))
// DecBrakeLevel;
}
break;
case Release_engine:
if (ReleaseEngine()) // zdana maszyna?
JumpToNextOrder();
break;
case Jump_to_first_order:
if (OrderPos > 1)
OrderPos = 1; // w zerowym zawsze jest czekanie
else
++OrderPos;
#if LOGORDERS
WriteLog("--> Jump_to_first_order");
OrdersDump();
#endif
break;
case Wait_for_orders: // jeśli czeka, też ma skanować, żeby odpalić się od semafora
/*
if ((mvOccupied->ActiveDir!=0))
{//jeśli jest wybrany kierunek jazdy, można ustalić prędkość jazdy
VelDesired=fVelMax; //wstępnie prędkość maksymalna dla pojazdu(-ów), będzie następnie
ograniczana
SetDriverPsyche(); //ustawia AccPreferred (potrzebne tu?)
//Ra: odczyt (ActualProximityDist), (VelNext) i (AccPreferred) z tabelki prędkosci
AccDesired=AccPreferred; //AccPreferred wynika z osobowości mechanika
VelNext=VelDesired; //maksymalna prędkość wynikająca z innych czynników niż
trajektoria ruchu
ActualProximityDist=scanmax; //funkcja Update() może pozostawić wartości bez zmian
//hm, kiedyś semafory wysyłały SetVelocity albo ShuntVelocity i ustawły tak VelSignal
- a teraz jak to zrobić?
TCommandType
comm=TableUpdate(mvOccupied->Vel,VelDesired,ActualProximityDist,VelNext,AccDesired);
//szukanie optymalnych wartości
}
*/
// break;
case Shunt:
case Obey_train:
case Connect:
case Disconnect:
case Change_direction: // tryby wymagające jazdy
case Change_direction | Shunt: // zmiana kierunku podczas manewrów
case Change_direction | Connect: // zmiana kierunku podczas podłączania
if (OrderList[OrderPos] != Obey_train) // spokojne manewry
{
VelSignal = Min0R(VelSignal, 40); // jeśli manewry, to ograniczamy prędkość
if (AIControllFlag)
{ // to poniżej tylko dla AI
if (iVehicleCount >= 0) // jeśli jest co odczepić
if (!(iDrivigFlags & movePress))
if (mvOccupied->Vel > 0.0)
if (!iCoupler) // jeśli nie ma wcześniej potrzeby podczepienia
{
SetVelocity(0, 0, stopJoin); // 1. faza odczepiania: zatrzymanie
// WriteLog("Zatrzymanie w celu odczepienia");
}
}
}
else
SetDriverPsyche(); // Ra: było w PrepareEngine(), potrzebne tu?
// no albo przypisujemy -WaitingExpireTime, albo porównujemy z WaitingExpireTime
// if
// ((VelSignal==0.0)&&(WaitingTime>WaitingExpireTime)&&(mvOccupied->RunningTrack.Velmax!=0.0))
if (OrderList[OrderPos] &
(Shunt | Obey_train | Connect)) // odjechać sam może tylko jeśli jest w trybie jazdy
{ // automatyczne ruszanie po odstaniu albo spod SBL
if ((VelSignal == 0.0) && (WaitingTime > 0.0) &&
(mvOccupied->RunningTrack.Velmax != 0.0))
{ // jeśli stoi, a upłynął czas oczekiwania i tor ma niezerową prędkość
/*
if (WriteLogFlag)
{
append(AIlogFile);
writeln(AILogFile,ElapsedTime:5:2,": ",Name," V=0 waiting time expired!
(",WaitingTime:4:1,")");
close(AILogFile);
}
*/
if ((OrderList[OrderPos] & (Obey_train | Shunt)) ?
(iDrivigFlags & moveStopHere) :
false)
WaitingTime = -WaitingExpireTime; // zakaz ruszania z miejsca bez otrzymania
// wolnej drogi
else if (mvOccupied->CategoryFlag & 1)
{ // jeśli pociąg
if (AIControllFlag)
{
PrepareEngine(); // zmieni ustawiony kierunek
SetVelocity(20, 20); // jak się nastał, to niech jedzie 20km/h
WaitingTime = 0.0;
fWarningDuration = 1.5; // a zatrąbić trochę
mvOccupied->WarningSignal = 1;
}
else
SetVelocity(20, 20); // użytkownikowi zezwalamy jechać
}
else
{ // samochód ma stać, aż dostanie odjazd, chyba że stoi przez kolizję
if (eStopReason == stopBlock)
if (pVehicles[0]->fTrackBlock > fDriverDist)
if (AIControllFlag)
{
PrepareEngine(); // zmieni ustawiony kierunek
SetVelocity(-1, -1); // jak się nastał, to niech jedzie
WaitingTime = 0.0;
}
else
SetVelocity(-1,
-1); // użytkownikowi pozwalamy jechać (samochodem?)
}
}
else if ((VelSignal == 0.0) && (VelNext > 0.0) && (mvOccupied->Vel < 1.0))
if (iCoupler ? true : (iDrivigFlags & moveStopHere) == 0) // Ra: tu jest coś nie
// tak, bo bez tego
// warunku ruszało w
// manewrowym !!!!
SetVelocity(VelNext, VelNext, stopSem); // omijanie SBL
} // koniec samoistnego odjeżdżania
if (AIControllFlag)
if ((HelpMeFlag) || (mvControlling->DamageFlag > 0))
{
HelpMeFlag = false;
/*
if (WriteLogFlag)
with Controlling do
{
append(AIlogFile);
writeln(AILogFile,ElapsedTime:5:2,": ",Name," HelpMe!
(",DamageFlag,")");
close(AILogFile);
}
*/
}
if (AIControllFlag)
if (OrderList[OrderPos] &
Change_direction) // może być zmieszane z jeszcze jakąś komendą
{ // sprobuj zmienic kierunek
SetVelocity(0, 0, stopDir); // najpierw trzeba się zatrzymać
if (mvOccupied->Vel < 0.1)
{ // jeśli się zatrzymał, to zmieniamy kierunek jazdy, a nawet kabinę/człon
Activation(); // ustawienie zadanego wcześniej kierunku i ewentualne
// przemieszczenie AI
PrepareEngine();
JumpToNextOrder(); // następnie robimy, co jest do zrobienia (Shunt albo
// Obey_train)
if (OrderList[OrderPos] & (Shunt | Connect)) // jeśli dalej mamy manewry
if ((iDrivigFlags & moveStopHere) == 0) // o ile nie stać w miejscu
{ // jechać od razu w przeciwną stronę i nie trąbić z tego tytułu
iDrivigFlags &= ~moveStartHorn; // bez trąbienia przed ruszeniem
SetVelocity(fShuntVelocity, fShuntVelocity); // to od razu jedziemy
}
// iDrivigFlags|=moveStartHorn; //a później już można trąbić
/*
if (WriteLogFlag)
{
append(AIlogFile);
writeln(AILogFile,ElapsedTime:5:2,": ",Name," Direction changed!");
close(AILogFile);
}
*/
}
// else
// VelSignal:=0.0; //na wszelki wypadek niech zahamuje
} // Change_direction (tylko dla AI)
// ustalanie zadanej predkosci
if (AIControllFlag) // jeśli prowadzi AI
if (!iEngineActive) // jeśli silnik nie odpalony, to próbować naprawić
if (OrderList[OrderPos] & (Change_direction | Connect | Disconnect | Shunt |
Obey_train)) // jeśli coś ma robić
PrepareEngine(); // to niech odpala do skutku
if (iDrivigFlags & moveActive) // jeśli może skanować sygnały i reagować na komendy
{ // jeśli jest wybrany kierunek jazdy, można ustalić prędkość jazdy
// Ra: tu by jeszcze trzeba było wstawić uzależnienie (VelDesired) od odległości od
// przeszkody
// no chyba żeby to uwzgldnić już w (ActualProximityDist)
VelDesired = fVelMax; // wstępnie prędkość maksymalna dla pojazdu(-ów), będzie
// następnie ograniczana
if (TrainParams) // jeśli ma rozkład
if (TrainParams->TTVmax > 0.0) // i ograniczenie w rozkładzie
VelDesired = Min0R(VelDesired,
TrainParams->TTVmax); // to nie przekraczać rozkladowej
SetDriverPsyche(); // ustawia AccPreferred (potrzebne tu?)
// Ra: odczyt (ActualProximityDist), (VelNext) i (AccPreferred) z tabelki prędkosci
AccDesired = AccPreferred; // AccPreferred wynika z osobowości mechanika
VelNext = VelDesired; // maksymalna prędkość wynikająca z innych czynników niż
// trajektoria ruchu
ActualProximityDist = scanmax; // funkcja Update() może pozostawić wartości bez
// zmian
// hm, kiedyś semafory wysyłały SetVelocity albo ShuntVelocity i ustawły tak
// VelSignal - a teraz jak to zrobić?
TCommandType comm = TableUpdate(VelDesired, ActualProximityDist, VelNext,
AccDesired); // szukanie optymalnych wartości
// if (VelSignal!=VelDesired) //jeżeli prędkość zalecana jest inna (ale tryb też
// może być inny)
switch (comm)
{ // ustawienie VelSignal - trochę proteza = do przemyślenia
case cm_Ready: // W4 zezwolił na jazdę
TableCheck(
scanmax); // ewentualne doskanowanie trasy za W4, który zezwolił na jazdę
TableUpdate(VelDesired, ActualProximityDist, VelNext,
AccDesired); // aktualizacja po skanowaniu
// if (comm!=cm_SetVelocity) //jeśli dalej jest kolejny W4, to ma zwrócić
// cm_SetVelocity
if (VelNext == 0.0)
break; // ale jak coś z przodu zamyka, to ma stać
if (iDrivigFlags & moveStopCloser)
VelSignal = -1.0; // niech jedzie, jak W4 puściło - nie, ma czekać na
// sygnał z sygnalizatora!
case cm_SetVelocity: // od wersji 357 semafor nie budzi wyłączonej lokomotywy
if (!(OrderList[OrderPos] &
~(Obey_train | Shunt))) // jedzie w dowolnym trybie albo Wait_for_orders
if (fabs(VelSignal) >=
1.0) // 0.1 nie wysyła się do samochodow, bo potem nie ruszą
PutCommand("SetVelocity", VelSignal, VelNext,
NULL); // komenda robi dodatkowe operacje
break;
case cm_ShuntVelocity: // od wersji 357 Tm nie budzi wyłączonej lokomotywy
if (!(OrderList[OrderPos] &
~(Obey_train | Shunt))) // jedzie w dowolnym trybie albo Wait_for_orders
PutCommand("ShuntVelocity", VelSignal, VelNext, NULL);
else if (iCoupler) // jeśli jedzie w celu połączenia
SetVelocity(VelSignal, VelNext);
break;
case cm_Command: // komenda z komórki
if (!(OrderList[OrderPos] &
~(Obey_train | Shunt))) // jedzie w dowolnym trybie albo Wait_for_orders
if (mvOccupied->Vel < 0.1) // dopiero jak stanie
// iDrivigFlags|=moveStopHere moveStopCloser) //chyba że stanął za daleko
// (SU46 w WK staje za daleko)
{
PutCommand(eSignNext->CommandGet(), eSignNext->ValueGet(1),
eSignNext->ValueGet(2), NULL);
eSignNext->StopCommandSent(); // się wykonało już
}
break;
}
if (VelNext == 0.0)
if (!(OrderList[OrderPos] &
~(Shunt | Connect))) // jedzie w Shunt albo Connect, albo Wait_for_orders
{ // jeżeli wolnej drogi nie ma, a jest w trybie manewrowym albo oczekiwania
// if
// ((OrderList[OrderPos]&Connect)?pVehicles[0]->fTrackBlock>ActualProximityDist:true)
// //pomiar odległości nie działa dobrze?
// w trybie Connect skanować do tyłu tylko jeśli przed kolejnym sygnałem nie
// ma taboru do podłączenia
// Ra 2F1H: z tym (fTrackBlock) to nie jest najlepszy pomysł, bo lepiej by
// było porównać z odległością od sygnalizatora z przodu
if ((OrderList[OrderPos] | Connect) ? pVehicles[0]->fTrackBlock > 2000 :
true)
if ((comm = BackwardScan()) != cm_Unknown) // jeśli w drugą można jechać
{ // należy sprawdzać odległość od znalezionego sygnalizatora,
// aby w przypadku prędkości 0.1 wyciągnąć najpierw skład za
// sygnalizator
// i dopiero wtedy zmienić kierunek jazdy, oczekując podania
// prędkości >0.5
if (comm == cm_Command) // jeśli komenda Shunt
iDrivigFlags |=
moveStopHere; // to ją odbierz bez przemieszczania się (np.
// odczep wagony po dopchnięciu do końca toru)
iDirectionOrder = -iDirection; // zmiana kierunku jazdy
OrderList[OrderPos] = TOrders(OrderList[OrderPos] |
Change_direction); // zmiana kierunku
// bez psucia
// kolejnych komend
}
}
double vel = mvOccupied->Vel; // prędkość w kierunku jazdy
if (iDirection * mvOccupied->V < 0)
vel = -vel; // ujemna, gdy jedzie w przeciwną stronę, niż powinien
if (VelDesired < 0.0)
VelDesired = fVelMax; // bo VelDesired<0 oznacza prędkość maksymalną
// Ra: jazda na widoczność
if (pVehicles[0]->fTrackBlock < 1000.0) // przy 300m stał z zapamiętaną kolizją
{ // Ra 2F3F: przy jeździe pociągowej nie powinien dojeżdżać do poprzedzającego
// składu
if ((mvOccupied->CategoryFlag & 1) ?
((OrderCurrentGet() & (Connect | Obey_train)) == Obey_train) :
false) // jeśli jesteśmy pociągiem a jazda pociągowa i nie ściąganie ze
// szlaku
{
pVehicles[0]->ABuScanObjects(pVehicles[0]->DirectionGet(),
1000.0); // skanowanie sprawdzające
// Ra 2F3F: i jest problem, jak droga za semaforem kieruje na jakiś pojazd
// (np. w Skwarkach na ET22)
if (pVehicles[0]->fTrackBlock < 1000.0) // i jeśli nadal coś jest
if (VelNext != 0.0) // a następny sygnał zezwala na jazdę
if (pVehicles[0]->fTrackBlock <
ActualProximityDist) // i jest bliżej (tu by trzeba było wstawić
// odległość do semafora, z pominięciem SBL
VelDesired = 0.0; // to stoimy
}
else
pVehicles[0]->ABuScanObjects(pVehicles[0]->DirectionGet(),
300.0); // skanowanie sprawdzające
}
// if (mvOccupied->Vel>=0.1) //o ile jedziemy; jak stoimy to też trzeba jakoś
// zatrzymywać
if ((iDrivigFlags & moveConnect) == 0) // przy końcówce podłączania nie hamować
{ // sprawdzenie jazdy na widoczność
TCoupling *c =
pVehicles[0]->MoverParameters->Couplers +
(pVehicles[0]->DirectionGet() > 0 ? 0 : 1); // sprzęg z przodu składu
if (c->Connected) // a mamy coś z przodu
if (c->CouplingFlag ==
0) // jeśli to coś jest podłączone sprzęgiem wirtualnym
{ // wyliczanie optymalnego przyspieszenia do jazdy na widoczność
double k = c->Connected->Vel; // prędkość pojazdu z przodu (zakładając,
// że jedzie w tę samą stronę!!!)
if (k < vel + 10) // porównanie modułów prędkości [km/h]
{ // zatroszczyć się trzeba, jeśli tamten nie jedzie znacząco szybciej
double d =
pVehicles[0]->fTrackBlock - 0.5 * vel -
fMaxProximityDist; // odległość bezpieczna zależy od prędkości
if (d < 0) // jeśli odległość jest zbyt mała
{ // AccPreferred=-0.9; //hamowanie maksymalne, bo jest za blisko
if (k < 10.0) // k - prędkość tego z przodu
{ // jeśli tamten porusza się z niewielką prędkością albo stoi
if (OrderCurrentGet() & Connect)
{ // jeśli spinanie, to jechać dalej
AccPreferred = 0.2; // nie hamuj
VelNext = VelDesired = 2.0; // i pakuj się na tamtego
}
else // a normalnie to hamować
{
AccPreferred = -1.0; // to hamuj maksymalnie
VelNext = VelDesired = 0.0; // i nie pakuj się na
// tamtego
}
}
else // jeśli oba jadą, to przyhamuj lekko i ogranicz prędkość
{
if (k < vel) // jak tamten jedzie wolniej
if (d < fBrakeDist) // a jest w drodze hamowania
{
if (AccPreferred > fAccThreshold)
AccPreferred =
fAccThreshold; // to przyhamuj troszkę
VelNext = VelDesired = int(k); // to chyba już sobie
// dohamuje według
// uznania
}
}
ReactionTime = 0.1; // orientuj się, bo jest goraco
}
else
{ // jeśli odległość jest większa, ustalić maksymalne możliwe
// przyspieszenie (hamowanie)
k = (k * k - vel * vel) / (25.92 * d); // energia kinetyczna
// dzielona przez masę i
// drogę daje
// przyspieszenie
if (k > 0.0)
k *= 1.5; // jedź szybciej, jeśli możesz
// double ak=(c->Connected->V>0?1.0:-1.0)*c->Connected->AccS;
// //przyspieszenie tamtego
if (d < fBrakeDist) // a jest w drodze hamowania
if (k < AccPreferred)
{ // jeśli nie ma innych powodów do wolniejszej jazdy
AccPreferred = k;
if (VelNext > c->Connected->Vel)
{
VelNext =
c->Connected
->Vel; // ograniczenie do prędkości tamtego
ActualProximityDist =
d; // i odległość od tamtego jest istotniejsza
}
ReactionTime = 0.2; // zwiększ czujność
}
#if LOGVELOCITY
WriteLog("Collision: AccPreferred=" + AnsiString(k));
#endif
}
}
}
}
// sprawdzamy możliwe ograniczenia prędkości
if (OrderCurrentGet() & (Shunt | Obey_train)) // w Connect nie, bo moveStopHere
// odnosi się do stanu po połączeniu
if (iDrivigFlags & moveStopHere) // jeśli ma czekać na wolną drogę
if (vel == 0.0) // a stoi
if (VelNext == 0.0) // a wyjazdu nie ma
VelDesired = 0.0; // to ma stać
if (fStopTime < 0) // czas postoju przed dalszą jazdą (np. na przystanku)
VelDesired = 0.0; // jak ma czekać, to nie ma jazdy
// else if (VelSignal<0)
// VelDesired=fVelMax; //ile fabryka dala (Ra: uwzględione wagony)
else if (VelSignal >= 0) // jeśli skład był zatrzymany na początku i teraz już może jechać
VelDesired = Min0R(VelDesired, VelSignal);
if (mvOccupied->RunningTrack.Velmax >=
0) // ograniczenie prędkości z trajektorii ruchu
VelDesired =
Min0R(VelDesired,
mvOccupied->RunningTrack.Velmax); // uwaga na ograniczenia szlakowej!
if (VelforDriver >= 0) // tu jest zero przy zmianie kierunku jazdy
VelDesired = Min0R(VelDesired, VelforDriver); // Ra: tu może być 40, jeśli
// mechanik nie ma znajomości
// szlaaku, albo kierowca jeździ
// 70
if (TrainParams)
if (TrainParams->CheckTrainLatency() < 5.0)
if (TrainParams->TTVmax > 0.0)
VelDesired = Min0R(
VelDesired,
TrainParams
->TTVmax); // jesli nie spozniony to nie przekraczać rozkladowej
if (VelDesired > 0.0)
if (VelNext > 0.0 || (iDrivigFlags & moveStopHere)==0)
{ // jeśli można jechać, to odpalić dźwięk kierownika oraz zamknąć drzwi w
// składzie, jeśli nie mamy czekać na sygnał też trzeba odpalić
if (iDrivigFlags & moveGuardSignal)
{ // komunikat od kierownika tu, bo musi być wolna droga i odczekany czas
// stania
iDrivigFlags &= ~moveGuardSignal; // tylko raz nadać
tsGuardSignal->Stop();
// w zasadzie to powinien mieć flagę, czy jest dźwiękiem radiowym, czy
// bezpośrednim
// albo trzeba zrobić dwa dźwięki, jeden bezpośredni, słyszalny w
// pobliżu, a drugi radiowy, słyszalny w innych lokomotywach
// na razie zakładam, że to nie jest dźwięk radiowy, bo trzeba by zrobić
// obsługę kanałów radiowych itd.
if (!iGuardRadio) // jeśli nie przez radio
tsGuardSignal->Play(
1.0, 0, !FreeFlyModeFlag,
pVehicle->GetPosition()); // dla true jest głośniej
else
// if (iGuardRadio==iRadioChannel) //zgodność kanału
// if (!FreeFlyModeFlag) //obserwator musi być w środku pojazdu
// (albo może mieć radio przenośne) - kierownik mógłby powtarzać
// przy braku reakcji
if (SquareMagnitude(pVehicle->GetPosition() -
Global::pCameraPosition) <
2000 * 2000) // w odległości mniejszej niż 2km
tsGuardSignal->Play(
1.0, 0, true,
pVehicle->GetPosition()); // dźwięk niby przez radio
}
if (iDrivigFlags & moveDoorOpened) // jeśli drzwi otwarte
if (!mvOccupied
->DoorOpenCtrl) // jeśli drzwi niesterowane przez maszynistę
Doors(false); // a EZT zamknie dopiero po odegraniu komunikatu
// kierownika
}
if (mvOccupied->V == 0.0)
AbsAccS = fAccGravity; // Ra 2014-03: jesli skład stoi, to działa na niego
// składowa styczna grawitacji
else
AbsAccS = iDirection * mvOccupied->AccS; // przyspieszenie chwilowe, liczone
// jako różnica skierowanej prędkości w
// czasie
// if (mvOccupied->V<0.0) AbsAccS=-AbsAccS; //Ra 2014-03: to trzeba przemyśleć
// if (vel<0) //jeżeli się stacza w tył; 2014-03: to jest bez sensu, bo vel>=0
// AbsAccS=-AbsAccS; //to przyspieszenie też działa wtedy w nieodpowiednią stronę
// AbsAccS+=fAccGravity; //wypadkowe przyspieszenie (czy to ma sens?)
#if LOGVELOCITY
// WriteLog("VelDesired="+AnsiString(VelDesired)+",
// VelSignal="+AnsiString(VelSignal));
WriteLog("Vel=" + AnsiString(vel) + ", AbsAccS=" + AnsiString(AbsAccS) +
", AccGrav=" + AnsiString(fAccGravity));
#endif
// ustalanie zadanego przyspieszenia
//(ActualProximityDist) - odległość do miejsca zmniejszenia prędkości
//(AccPreferred) - wynika z psychyki oraz uwzglęnia już ewentualne zderzenie z
// pojazdem z przodu, ujemne gdy należy hamować
//(AccDesired) - uwzględnia sygnały na drodze ruchu, ujemne gdy należy hamować
//(fAccGravity) - chwilowe przspieszenie grawitacyjne, ujemne działa przeciwnie do
// zadanego kierunku jazdy
//(AbsAccS) - chwilowe przyspieszenie pojazu (uwzględnia grawitację), ujemne działa
// przeciwnie do zadanego kierunku jazdy
//(AccDesired) porównujemy z (fAccGravity) albo (AbsAccS)
// if ((VelNext>=0.0)&&(ActualProximityDist>=0)&&(mvOccupied->Vel>=VelNext)) //gdy
// zbliza sie i jest za szybko do NOWEGO
if ((VelNext >= 0.0) && (ActualProximityDist <= scanmax) && (vel >= VelNext))
{ // gdy zbliża się i jest za szybki do nowej prędkości, albo stoi na zatrzymaniu
if (vel > 0.0)
{ // jeśli jedzie
if ((vel < VelNext) ?
(ActualProximityDist > fMaxProximityDist * (1 + 0.1 * vel)) :
false) // dojedz do semafora/przeszkody
{ // jeśli jedzie wolniej niż można i jest wystarczająco daleko, to można
// przyspieszyć
if (AccPreferred > 0.0) // jeśli nie ma zawalidrogi
AccDesired = AccPreferred;
// VelDesired:=Min0R(VelDesired,VelReduced+VelNext);
}
else if (ActualProximityDist > fMinProximityDist)
{ // jedzie szybciej, niż trzeba na końcu ActualProximityDist, ale jeszcze
// jest daleko
if (vel <
VelNext + 40.0) // dwustopniowe hamowanie - niski przy małej różnicy
{ // jeśli jedzie wolniej niż VelNext+35km/h //Ra: 40, żeby nie
// kombinował na zwrotnicach
if (VelNext == 0.0)
{ // jeśli ma się zatrzymać, musi być to robione precyzyjnie i
// skutecznie
if (ActualProximityDist <
fMaxProximityDist) // jak minął już maksymalny dystans
{ // po prostu hamuj (niski stopień) //ma stanąć, a jest w
// drodze hamowania albo ma jechać
AccDesired = fAccThreshold; // hamowanie tak, aby stanąć
VelDesired = 0.0; // Min0R(VelDesired,VelNext);
}
else if (ActualProximityDist > fBrakeDist)
{ // jeśli ma stanąć, a mieści się w drodze hamowania
if (vel < 10.0) // jeśli prędkość jest łatwa do zatrzymania
{ // tu jest trochę problem, bo do punktu zatrzymania dobija
// na raty
// AccDesired=AccDesired<0.0?0.0:0.1*AccPreferred;
AccDesired = AccPreferred; // proteza trochę; jak tu
// wychodzi 0.05, to loki
// mają problem utrzymać
// takie przyspieszenie
}
else if (vel <= 30.0) // trzymaj 30 km/h
AccDesired = Min0R(0.5 * AccDesired,
AccPreferred); // jak jest tu 0.5, to
// samochody się
// dobijają do siebie
else
AccDesired = 0.0;
}
else // 25.92 (=3.6*3.6*2) - przelicznik z km/h na m/s
if (vel <
VelNext + fVelPlus) // jeśli niewielkie przekroczenie
// AccDesired=0.0;
AccDesired = Min0R(0.0, AccPreferred); // proteza trochę: to
// niech nie hamuje,
// chyba że coś z
// przodu
else
AccDesired = -(vel * vel) /
(25.92 * (ActualProximityDist +
0.1)); //-fMinProximityDist));//-0.1;
////mniejsze opóźnienie przy
// małej różnicy
ReactionTime = 0.1; // i orientuj się szybciej, jak masz stanąć
}
else if (vel < VelNext + fVelPlus) // jeśli niewielkie
// przekroczenie, ale ma jechać
AccDesired =
Min0R(0.0, AccPreferred); // to olej (zacznij luzować)
else
{ // jeśli większe przekroczenie niż fVelPlus [km/h], ale ma jechać
// Ra 2F1I: jak było (VelNext+fVelPlus) tu, to hamował zbyt
// późno przed 40, a potem zbyt mocno i zwalniał do 30
AccDesired = (VelNext * VelNext - vel * vel) /
(25.92 * ActualProximityDist +
0.1); // mniejsze opóźnienie przy małej różnicy
if (ActualProximityDist < fMaxProximityDist)
ReactionTime = 0.1; // i orientuj się szybciej, jeśli w
// krytycznym przedziale
}
}
else // przy dużej różnicy wysoki stopień (1,25 potrzebnego opoznienia)
AccDesired = (VelNext * VelNext - vel * vel) /
(20.73 * ActualProximityDist +
0.1); // najpierw hamuje mocniej, potem zluzuje
if (AccPreferred < AccDesired)
AccDesired = AccPreferred; //(1+abs(AccDesired))
// ReactionTime=0.5*mvOccupied->BrakeDelay[2+2*mvOccupied->BrakeDelayFlag];
// //aby szybkosc hamowania zalezala od przyspieszenia i opoznienia
// hamulcow
// fBrakeTime=0.5*mvOccupied->BrakeDelay[2+2*mvOccupied->BrakeDelayFlag];
// //aby szybkosc hamowania zalezala od przyspieszenia i opoznienia
// hamulcow
}
else
{ // jest bliżej niż fMinProximityDist
VelDesired =
Min0R(VelDesired, VelNext); // utrzymuj predkosc bo juz blisko
if (vel <
VelNext + fVelPlus) // jeśli niewielkie przekroczenie, ale ma jechać
AccDesired = Min0R(0.0, AccPreferred); // to olej (zacznij luzować)
ReactionTime = 0.1; // i orientuj się szybciej
}
}
else // zatrzymany (vel==0.0)
// if (iDrivigFlags&moveStopHere) //to nie dotyczy podczepiania
// if ((VelNext>0.0)||(ActualProximityDist>fMaxProximityDist*1.2))
if (VelNext > 0.0)
AccDesired = AccPreferred; // można jechać
else // jeśli daleko jechać nie można
if (ActualProximityDist >
fMaxProximityDist) // ale ma kawałek do sygnalizatora
{ // if ((iDrivigFlags&moveStopHere)?false:AccPreferred>0)
if (AccPreferred > 0)
AccDesired = AccPreferred; // dociagnij do semafora;
else
VelDesired = 0.0; //,AccDesired=-fabs(fAccGravity); //stoj (hamuj z siłą
// równą składowej stycznej grawitacji)
}
else
VelDesired = 0.0; // VelNext=0 i stoi bliżej niż fMaxProximityDist
}
else // gdy jedzie wolniej niż potrzeba, albo nie ma przeszkód na drodze
AccDesired = (VelDesired != 0.0 ? AccPreferred : -0.01); // normalna jazda
// koniec predkosci nastepnej
if ((VelDesired >= 0.0) &&
(vel > VelDesired)) // jesli jedzie za szybko do AKTUALNEGO
if (VelDesired == 0.0) // jesli stoj, to hamuj, ale i tak juz za pozno :)
AccDesired = -0.9; // hamuj solidnie
else if ((vel < VelDesired + fVelPlus)) // o 5 km/h to olej
{
if ((AccDesired > 0.0))
AccDesired = 0.0;
}
else
AccDesired = fAccThreshold; // hamuj tak średnio
// koniec predkosci aktualnej
if (fAccThreshold > -0.3) // bez sensu, ale dla towarowych korzystnie
{ // Ra 2014-03: to nie uwzględnia odległości i zaczyna hamować, jak tylko zobaczy
// W4
if ((AccDesired > 0.0) &&
(VelNext >= 0.0)) // wybieg bądź lekkie hamowanie, warunki byly zamienione
if (vel > VelNext + 100.0) // lepiej zaczac hamowac
AccDesired = fAccThreshold;
else if (vel > VelNext + 70.0)
AccDesired = 0.0; // nie spiesz się, bo będzie hamowanie
// koniec wybiegu i hamowania
}
if (AIControllFlag)
{ // część wykonawcza tylko dla AI, dla człowieka jedynie napisy
if (mvControlling->ConvOvldFlag ||
!mvControlling->Mains) // WS może wywalić z powodu błędu w drutach
{ // wywalił bezpiecznik nadmiarowy przetwornicy
// while (DecSpeed()); //zerowanie napędu
// Controlling->ConvOvldFlag=false; //reset nadmiarowego
PrepareEngine(); // próba ponownego załączenia
}
// włączanie bezpiecznika
if ((mvControlling->EngineType == ElectricSeriesMotor) ||
(mvControlling->TrainType & dt_EZT) ||
(mvControlling->EngineType == DieselElectric))
if (mvControlling->FuseFlag || Need_TryAgain)
{
Need_TryAgain =
false; // true, jeśli druga pozycja w elektryku nie załapała
// if (!Controlling->DecScndCtrl(1)) //kręcenie po mału
// if (!Controlling->DecMainCtrl(1)) //nastawnik jazdy na 0
mvControlling->DecScndCtrl(2); // nastawnik bocznikowania na 0
mvControlling->DecMainCtrl(2); // nastawnik jazdy na 0
mvControlling->MainSwitch(
true); // Ra: dodałem, bo EN57 stawały po wywaleniu
if (!mvControlling->FuseOn())
HelpMeFlag = true;
else
{
++iDriverFailCount;
if (iDriverFailCount > maxdriverfails)
Psyche = Easyman;
if (iDriverFailCount > maxdriverfails * 2)
SetDriverPsyche();
}
}
if (mvOccupied->BrakeSystem == Pneumatic) // napełnianie uderzeniowe
if (mvOccupied->BrakeHandle == FV4a)
{
if (mvOccupied->BrakeCtrlPos == -2)
mvOccupied->BrakeLevelSet(0);
// if
// ((mvOccupied->BrakeCtrlPos<0)&&(mvOccupied->PipeBrakePress<0.01))//{(CntrlPipePress-(Volume/BrakeVVolume/10)<0.01)})
// mvOccupied->IncBrakeLevel();
if ((mvOccupied->PipePress < 3.0) && (AccDesired > -0.03))
mvOccupied->BrakeReleaser(1);
if ((mvOccupied->BrakeCtrlPos == 0) && (AbsAccS < 0.0) &&
(AccDesired > -0.03))
// if FuzzyLogicAI(CntrlPipePress-PipePress,0.01,1))
// if
// ((mvOccupied->BrakePress>0.5)&&(mvOccupied->LocalBrakePos<0.5))//{((Volume/BrakeVVolume/10)<0.485)})
if ((mvOccupied->EqvtPipePress < 4.95) &&
(fReady > 0.35)) //{((Volume/BrakeVVolume/10)<0.485)})
{
if (iDrivigFlags &
moveOerlikons) // a reszta składu jest na to gotowa
mvOccupied->BrakeLevelSet(-1); // napełnianie w Oerlikonie
}
else if (Need_BrakeRelease)
{
Need_BrakeRelease = false;
mvOccupied->BrakeReleaser(1);
// DecBrakeLevel(); //z tym by jeszcze miało jakiś sens
}
// if
// ((mvOccupied->BrakeCtrlPos<0)&&(mvOccupied->BrakePress<0.3))//{(CntrlPipePress-(Volume/BrakeVVolume/10)<0.01)})
if ((mvOccupied->BrakeCtrlPos < 0) &&
(mvOccupied->EqvtPipePress >
(fReady < 0.25 ?
5.1 :
5.2))) //{(CntrlPipePress-(Volume/BrakeVVolume/10)<0.01)})
mvOccupied->IncBrakeLevel();
}
#if LOGVELOCITY
WriteLog("Dist=" + FloatToStrF(ActualProximityDist, ffFixed, 7, 1) +
", VelDesired=" + FloatToStrF(VelDesired, ffFixed, 7, 1) +
", AccDesired=" + FloatToStrF(AccDesired, ffFixed, 7, 3) +
", VelSignal=" + AnsiString(VelSignal) + ", VelNext=" +
AnsiString(VelNext));
#endif
if (AccDesired > 0.1)
if (vel < 10.0) // Ra 2F1H: jeśli prędkość jest mała, a można przyspieszać,
// to nie ograniczać przyspieszenia do 0.5m/ss
AccDesired = 0.9; // przy małych prędkościach może być trudno utrzymać
// małe przyspieszenie
// Ra 2F1I: wyłączyć kiedyś to uśrednianie i przeanalizować skanowanie, czemu
// migocze
if (AccDesired > -0.15) // hamowania lepeiej nie uśredniać
AccDesired = fAccDesiredAv =
0.2 * AccDesired +
0.8 * fAccDesiredAv; // uśrednione, żeby ograniczyć migotanie
if (VelDesired == 0.0)
if (AccDesired >= -0.01)
AccDesired = -0.01; // Ra 2F1J: jeszcze jedna prowizoryczna łatka
if (AccDesired >= 0.0)
if (iDrivigFlags & movePress)
mvOccupied->BrakeReleaser(1); // wyluzuj lokomotywę - może być więcej!
else if (OrderList[OrderPos] !=
Disconnect) // przy odłączaniu nie zwalniamy tu hamulca
if ((AccDesired > 0.0) ||
(fAccGravity * fAccGravity <
0.001)) // luzuj tylko na plaskim lub przy ruszaniu
{
while (DecBrake())
; // jeśli przyspieszamy, to nie hamujemy
if (mvOccupied->BrakePress > 0.4)
mvOccupied->BrakeReleaser(
1); // wyluzuj lokomotywę, to szybciej ruszymy
}
// margines dla prędkości jest doliczany tylko jeśli oczekiwana prędkość jest
// większa od 5km/h
if (!(iDrivigFlags & movePress))
{ // jeśli nie dociskanie
if (AccDesired < -0.1)
while (DecSpeed())
; // jeśli hamujemy, to nie przyspieszamy
else if (((fAccGravity < -0.01) ? AccDesired < 0.0 :
AbsAccS > AccDesired) ||
(vel > VelDesired)) // jak za bardzo przyspiesza albo prędkość
// przekroczona
DecSpeed(); // pojedyncze cofnięcie pozycji, bo na zero to przesada
}
// yB: usunięte różne dziwne warunki, oddzielamy część zadającą od wykonawczej
// zwiekszanie predkosci
// Ra 2F1H: jest konflikt histerezy pomiędzy nastawioną pozycją a uzyskiwanym
// przyspieszeniem - utrzymanie pozycji powoduje przekroczenie przyspieszenia
if (AbsAccS <
AccDesired) // jeśli przyspieszenie pojazdu jest mniejsze niż żądane oraz
if (vel < VelDesired - fVelMinus) // jeśli prędkość w kierunku czoła jest
// mniejsza od dozwolonej o margines
if ((ActualProximityDist > fMaxProximityDist) ? true : (vel < VelNext))
IncSpeed(); // to można przyspieszyć
// if ((AbsAccS<AccDesired)&&(vel<VelDesired))
// if (!MaxVelFlag) //Ra: to nie jest używane
// if (!IncSpeed()) //Ra: to tutaj jest bez sensu, bo nie dociągnie do
// bezoporowej
// MaxVelFlag=true; //Ra: to nie jest używane
// else
// MaxVelFlag=false; //Ra: to nie jest używane
// if (Vel<VelDesired*0.85) and (AccDesired>0) and
// (EngineType=ElectricSeriesMotor)
// and (RList[MainCtrlPos].R>0.0) and (not DelayCtrlFlag))
// if (Im<Imin) and Ready=True {(BrakePress<0.01*MaxBrakePress)})
// IncMainCtrl(1); //zwieksz nastawnik skoro możesz - tak aby się ustawic na
// bezoporowej
// yB: usunięte różne dziwne warunki, oddzielamy część zadającą od wykonawczej
// zmniejszanie predkosci
if (mvOccupied->TrainType &
dt_EZT) // właściwie, to warunek powinien być na działający EP
{ // Ra: to dobrze hamuje EP w EZT
if ((AccDesired <= fAccThreshold) ? // jeśli hamować - u góry ustawia się
// hamowanie na fAccThreshold
((AbsAccS > AccDesired) || (mvOccupied->BrakeCtrlPos < 0)) :
false) // hamować bardziej, gdy aktualne opóźnienie hamowania
// mniejsze niż (AccDesired)
IncBrake();
else if (OrderList[OrderPos] !=
Disconnect) // przy odłączaniu nie zwalniamy tu hamulca
if (AbsAccS <
AccDesired -
0.05) // jeśli opóźnienie większe od wymaganego (z histerezą)
{ // luzowanie, gdy za dużo
if (mvOccupied->BrakeCtrlPos >= 0)
DecBrake(); // tutaj zmniejszało o 1 przy odczepianiu
}
else if (mvOccupied->Handle->TimeEP)
{
if (mvOccupied->Handle->GetPos(bh_EPR) -
mvOccupied->Handle->GetPos(bh_EPN) <
0.1)
mvOccupied->SwitchEPBrake(0);
else
mvOccupied->BrakeLevelSet(mvOccupied->Handle->GetPos(bh_EPN));
}
// else if (mvOccupied->BrakeCtrlPos<0) IncBrake(); //ustawienie
// jazdy (pozycja 0)
// else if (mvOccupied->BrakeCtrlPos>0) DecBrake();
}
else
{ // a stara wersja w miarę dobrze działa na składy wagonowe
// if (mvOccupied->Handle->Time)
// mvOccupied->BrakeLevelSet(mvOccupied->Handle->GetPos(bh_MB));
// //najwyzej sobie przestawi
if (((fAccGravity < -0.05) && (vel < 0)) ||
((AccDesired < fAccGravity - 0.1) &&
(AbsAccS >
AccDesired + 0.05))) // u góry ustawia się hamowanie na fAccThreshold
// if not MinVelFlag)
if (fBrakeTime < 0 ? true : (AccDesired < fAccGravity - 0.3) ||
(mvOccupied->BrakeCtrlPos <= 0))
if (!IncBrake()) // jeśli upłynął czas reakcji hamulca, chyba że
// nagłe albo luzował
MinVelFlag = true;
else
{
MinVelFlag = false;
fBrakeTime =
3 +
0.5 *
(mvOccupied
->BrakeDelay[2 + 2 * mvOccupied->BrakeDelayFlag] -
3);
// Ra: ten czas należy zmniejszyć, jeśli czas dojazdu do
// zatrzymania jest mniejszy
fBrakeTime *= 0.5; // Ra: tymczasowo, bo przeżyna S1
}
if ((AccDesired < fAccGravity - 0.05) && (AbsAccS < AccDesired - 0.2))
// if ((AccDesired<0.0)&&(AbsAccS<AccDesired-0.1)) //ST44 nie hamuje na
// czas, 2-4km/h po minięciu tarczy
// if (fBrakeTime<0)
{ // jak hamuje, to nie tykaj kranu za często
// yB: luzuje hamulec dopiero przy różnicy opóźnień rzędu 0.2
if (OrderList[OrderPos] !=
Disconnect) // przy odłączaniu nie zwalniamy tu hamulca
DecBrake(); // tutaj zmniejszało o 1 przy odczepianiu
fBrakeTime =
(mvOccupied->BrakeDelay[1 + 2 * mvOccupied->BrakeDelayFlag]) / 3.0;
fBrakeTime *= 0.5; // Ra: tymczasowo, bo przeżyna S1
}
}
// Mietek-end1
SpeedSet(); // ciągla regulacja prędkości
#if LOGVELOCITY
WriteLog("BrakePos=" + AnsiString(mvOccupied->BrakeCtrlPos) + ", MainCtrl=" +
AnsiString(mvControlling->MainCtrlPos));
#endif
/* //Ra: mamy teraz wskażnik na człon silnikowy, gorzej jak są dwa w
ukrotnieniu...
//zapobieganie poslizgowi w czlonie silnikowym; Ra: Couplers[1] powinno
być
if (Controlling->Couplers[0].Connected!=NULL)
if (TestFlag(Controlling->Couplers[0].CouplingFlag,ctrain_controll))
if (Controlling->Couplers[0].Connected->SlippingWheels)
if (Controlling->ScndCtrlPos>0?!Controlling->DecScndCtrl(1):true)
{
if (!Controlling->DecMainCtrl(1))
if (mvOccupied->BrakeCtrlPos==mvOccupied->BrakeCtrlPosNo)
mvOccupied->DecBrakeLevel();
++iDriverFailCount;
}
*/
// zapobieganie poslizgowi u nas
if (mvControlling->SlippingWheels)
{
if (!mvControlling->DecScndCtrl(2)) // bocznik na zero
mvControlling->DecMainCtrl(1);
if (mvOccupied->BrakeCtrlPos ==
mvOccupied->BrakeCtrlPosNo) // jeśli ostatnia pozycja hamowania
mvOccupied->DecBrakeLevel(); // to cofnij hamulec
else
mvControlling->AntiSlippingButton();
++iDriverFailCount;
mvControlling->SlippingWheels = false; // flaga już wykorzystana
}
if (iDriverFailCount > maxdriverfails)
{
Psyche = Easyman;
if (iDriverFailCount > maxdriverfails * 2)
SetDriverPsyche();
}
} // if (AIControllFlag)
else
{ // tu mozna dać komunikaty tekstowe albo słowne: przyspiesz, hamuj (lekko,
// średnio, mocno)
}
} // kierunek różny od zera
else
{ // tutaj, gdy pojazd jest wyłączony
if (!AIControllFlag) // jeśli sterowanie jest w gestii użytkownika
if (mvOccupied->Battery) // czy użytkownik załączył baterię?
if (mvOccupied->ActiveDir) // czy ustawił kierunek
{ // jeśli tak, to uruchomienie skanowania
CheckVehicles(); // sprawdzić skład
TableClear(); // resetowanie tabelki skanowania
PrepareEngine(); // uruchomienie
}
}
if (AIControllFlag)
{ // odhamowywanie składu po zatrzymaniu i zabezpieczanie lokomotywy
if ((OrderList[OrderPos] & (Disconnect | Connect)) ==
0) // przy (p)odłączaniu nie zwalniamy tu hamulca
if ((mvOccupied->V == 0.0) && ((VelDesired == 0.0) || (AccDesired == 0.0)))
if ((mvOccupied->BrakeCtrlPos < 1) || !mvOccupied->DecBrakeLevel())
mvOccupied->IncLocalBrakeLevel(1); // dodatkowy na pozycję 1
}
break; // rzeczy robione przy jezdzie
} // switch (OrderList[OrderPos])
// kasowanie licznika czasu
LastReactionTime = 0.0;
UpdateOK = true;
} // if ((LastReactionTime>Min0R(ReactionTime,2.0)))
else
LastReactionTime += dt;
if ((fLastStopExpDist > 0.0) && (mvOccupied->DistCounter > fLastStopExpDist))
{
iStationStart = TrainParams->StationIndex; // zaktualizować wyświetlanie rozkładu
fLastStopExpDist = -1.0f; // usunąć licznik
}
if (AIControllFlag)
{
if (fWarningDuration > 0.0) // jeśli pozostało coś do wytrąbienia
{ // trąbienie trwa nadal
fWarningDuration = fWarningDuration - dt;
if (fWarningDuration < 0.05)
mvOccupied->WarningSignal = 0; // a tu się kończy
if (ReactionTime > fWarningDuration)
ReactionTime =
fWarningDuration; // wcześniejszy przebłysk świadomości, by zakończyć trąbienie
}
if (mvOccupied->Vel >=
3.0) // jesli jedzie, można odblokować trąbienie, bo się wtedy nie włączy
{
iDrivigFlags &= ~moveStartHornDone; // zatrąbi dopiero jak następnym razem stanie
iDrivigFlags |= moveStartHorn; // i trąbić przed następnym ruszeniem
}
return UpdateOK;
}
else // if (AIControllFlag)
return false; // AI nie obsługuje
}
void TController::JumpToNextOrder()
{ // wykonanie kolejnej komendy z tablicy rozkazów
if (OrderList[OrderPos] != Wait_for_orders)
{
if (OrderList[OrderPos] & Change_direction) // jeśli zmiana kierunku
if (OrderList[OrderPos] != Change_direction) // ale nałożona na coś
{
OrderList[OrderPos] =
TOrders(OrderList[OrderPos] &
~Change_direction); // usunięcie zmiany kierunku z innej komendy
OrderCheck();
return;
}
if (OrderPos < maxorders - 1)
++OrderPos;
else
OrderPos = 0;
}
OrderCheck();
#if LOGORDERS
WriteLog("--> JumpToNextOrder");
OrdersDump(); // normalnie nie ma po co tego wypisywać
#endif
};
void TController::JumpToFirstOrder()
{ // taki relikt
OrderPos = 1;
if (OrderTop == 0)
OrderTop = 1;
OrderCheck();
#if LOGORDERS
WriteLog("--> JumpToFirstOrder");
OrdersDump(); // normalnie nie ma po co tego wypisywać
#endif
};
void TController::OrderCheck()
{ // reakcja na zmianę rozkazu
if (OrderList[OrderPos] & (Shunt | Connect | Obey_train))
CheckVehicles(); // sprawdzić światła
if (OrderList[OrderPos] & Change_direction) // może być nałożona na inną i wtedy ma priorytet
iDirectionOrder = -iDirection; // trzeba zmienić jawnie, bo się nie domyśli
else if (OrderList[OrderPos] == Obey_train)
iDrivigFlags |= moveStopPoint; // W4 są widziane
else if (OrderList[OrderPos] == Disconnect)
iVehicleCount = iVehicleCount < 0 ? 0 : iVehicleCount; // odczepianie lokomotywy
else if (OrderList[OrderPos] == Connect)
iDrivigFlags &= ~moveStopPoint; // podczas jazdy na połączenie nie zwracać uwagi na W4
else if (OrderList[OrderPos] == Wait_for_orders)
OrdersClear(); // czyszczenie rozkazów i przeskok do zerowej pozycji
}
void TController::OrderNext(TOrders NewOrder)
{ // ustawienie rozkazu do wykonania jako następny
if (OrderList[OrderPos] == NewOrder)
return; // jeśli robi to, co trzeba, to koniec
if (!OrderPos)
OrderPos = 1; // na pozycji zerowej pozostaje czekanie
OrderTop = OrderPos; // ale może jest czymś zajęty na razie
if (NewOrder >= Shunt) // jeśli ma jechać
{ // ale może być zajęty chwilowymi operacjami
while (OrderList[OrderTop] ? OrderList[OrderTop] < Shunt : false) // jeśli coś robi
++OrderTop; // pomijamy wszystkie tymczasowe prace
}
else
{ // jeśli ma ustawioną jazdę, to wyłączamy na rzecz operacji
while (OrderList[OrderTop] ?
(OrderList[OrderTop] < Shunt) && (OrderList[OrderTop] != NewOrder) :
false) // jeśli coś robi
++OrderTop; // pomijamy wszystkie tymczasowe prace
}
OrderList[OrderTop++] = NewOrder; // dodanie rozkazu jako następnego
#if LOGORDERS
WriteLog("--> OrderNext");
OrdersDump(); // normalnie nie ma po co tego wypisywać
#endif
}
void TController::OrderPush(TOrders NewOrder)
{ // zapisanie na stosie kolejnego rozkazu do wykonania
if (OrderPos == OrderTop) // jeśli miałby być zapis na aktalnej pozycji
if (OrderList[OrderPos] < Shunt) // ale nie jedzie
++OrderTop; // niektóre operacje muszą zostać najpierw dokończone => zapis na kolejnej
if (OrderList[OrderTop] != NewOrder) // jeśli jest to samo, to nie dodajemy
OrderList[OrderTop++] = NewOrder; // dodanie rozkazu na stos
// if (OrderTop<OrderPos) OrderTop=OrderPos;
if (OrderTop >= maxorders)
ErrorLog("Commands overflow: The program will now crash");
#if LOGORDERS
WriteLog("--> OrderPush");
OrdersDump(); // normalnie nie ma po co tego wypisywać
#endif
}
void TController::OrdersDump()
{ // wypisanie kolejnych rozkazów do logu
WriteLog("Orders for " + pVehicle->asName + ":");
for (int b = 0; b < maxorders; ++b)
{
WriteLog(AnsiString(b) + ": " + Order2Str(OrderList[b]) + (OrderPos == b ? " <-" : ""));
if (b) // z wyjątkiem pierwszej pozycji
if (OrderList[b] == Wait_for_orders) // jeśli końcowa komenda
break; // dalej nie trzeba
}
};
TOrders TController::OrderCurrentGet()
{
return OrderList[OrderPos];
}
TOrders TController::OrderNextGet()
{
return OrderList[OrderPos + 1];
}
void TController::OrdersInit(double fVel)
{ // wypełnianie tabelki rozkazów na podstawie rozkładu
// ustawienie kolejności komend, niezależnie kto prowadzi
// Mechanik->OrderPush(Wait_for_orders); //czekanie na lepsze czasy
// OrderPos=OrderTop=0; //wypełniamy od pozycji 0
OrdersClear(); // usunięcie poprzedniej tabeli
OrderPush(Prepare_engine); // najpierw odpalenie silnika
if (TrainParams->TrainName == AnsiString("none"))
{ // brak rozkładu to jazda manewrowa
if (fVel > 0.05) // typowo 0.1 oznacza gotowość do jazdy, 0.01 tylko załączenie silnika
OrderPush(Shunt); // jeśli nie ma rozkładu, to manewruje
}
else
{ // jeśli z rozkładem, to jedzie na szlak
if ((fVel > 0.0) && (fVel < 0.02))
OrderPush(Shunt); // dla prędkości 0.01 włączamy jazdę manewrową
else if (TrainParams ?
(TrainParams->TimeTable[1].StationWare.Pos(
"@") ? // jeśli obrót na pierwszym przystanku
((iDrivigFlags &
movePushPull) ? // SZT również! SN61 zależnie od wagonów...
(TrainParams->TimeTable[1].StationName == TrainParams->Relation1) :
false) :
false) :
true)
OrderPush(Shunt); // a teraz start będzie w manewrowym, a tryb pociągowy włączy W4
else
// jeśli start z pierwszej stacji i jednocześnie jest na niej zmiana kierunku, to EZT ma
// mieć Shunt
OrderPush(Obey_train); // dla starych scenerii start w trybie pociagowym
if (DebugModeFlag) // normalnie nie ma po co tego wypisywać
WriteLog("/* Timetable: " + TrainParams->ShowRelation());
TMTableLine *t;
for (int i = 0; i <= TrainParams->StationCount; ++i)
{
t = TrainParams->TimeTable + i;
if (DebugModeFlag) // normalnie nie ma po co tego wypisywać
WriteLog(AnsiString(t->StationName) + " " + AnsiString((int)t->Ah) + ":" +
AnsiString((int)t->Am) + ", " + AnsiString((int)t->Dh) + ":" +
AnsiString((int)t->Dm) + " " + AnsiString(t->StationWare));
if (AnsiString(t->StationWare).Pos("@"))
{ // zmiana kierunku i dalsza jazda wg rozkładu
if (iDrivigFlags & movePushPull) // SZT również! SN61 zależnie od wagonów...
{ // jeśli skład zespolony, wystarczy zmienić kierunek jazdy
OrderPush(Change_direction); // zmiana kierunku
}
else
{ // dla zwykłego składu wagonowego odczepiamy lokomotywę
OrderPush(Disconnect); // odczepienie lokomotywy
OrderPush(Shunt); // a dalej manewry
if (i <= TrainParams->StationCount) // 130827: to się jednak nie sprawdza
{ //"@" na ostatniej robi tylko odpięcie
// OrderPush(Change_direction); //zmiana kierunku
// OrderPush(Shunt); //jazda na drugą stronę składu
// OrderPush(Change_direction); //zmiana kierunku
// OrderPush(Connect); //jazda pod wagony
}
}
if (i < TrainParams->StationCount) // jak nie ostatnia stacja
OrderPush(Obey_train); // to dalej wg rozkładu
}
}
if (DebugModeFlag) // normalnie nie ma po co tego wypisywać
WriteLog("*/");
OrderPush(Shunt); // po wykonaniu rozkładu przełączy się na manewry
}
// McZapkie-100302 - to ma byc wyzwalane ze scenerii
if (fVel == 0.0)
SetVelocity(0, 0, stopSleep); // jeśli nie ma prędkości początkowej, to śpi
else
{ // jeśli podana niezerowa prędkość
if ((fVel >= 1.0) ||
(fVel < 0.02)) // jeśli ma jechać - dla 0.01 ma podjechać manewrowo po podaniu sygnału
iDrivigFlags = (iDrivigFlags & ~moveStopHere) |
moveStopCloser; // to do następnego W4 ma podjechać blisko
else
iDrivigFlags |= moveStopHere; // czekać na sygnał
JumpToFirstOrder();
if (fVel >= 1.0) // jeśli ma jechać
SetVelocity(fVel, -1); // ma ustawić żądaną prędkość
else
SetVelocity(0, 0, stopSleep); // prędkość w przedziale (0;1) oznacza, że ma stać
}
#if LOGORDERS
WriteLog("--> OrdersInit");
#endif
if (DebugModeFlag) // normalnie nie ma po co tego wypisywać
OrdersDump(); // wypisanie kontrolne tabelki rozkazów
// McZapkie! - zeby w ogole AI ruszyl to musi wykonac powyzsze rozkazy
// Ale mozna by je zapodac ze scenerii
};
AnsiString TController::StopReasonText()
{ // informacja tekstowa o przyczynie zatrzymania
if (eStopReason != 7) // zawalidroga będzie inaczej
return StopReasonTable[eStopReason];
else
return "Blocked by " + (pVehicles[0]->PrevAny()->GetName());
};
//----------------------------------------------------------------------------------------------------------------------
// McZapkie: skanowanie semaforów
// Ra: stare funkcje skanujące, używane podczas manewrów do szukania sygnalizatora z tyłu
//- nie reagują na PutValues, bo nie ma takiej potrzeby
//- rozpoznają tylko zerową prędkość (jako koniec toru i brak podstaw do dalszego skanowania)
//----------------------------------------------------------------------------------------------------------------------
/* //nie używane
double TController::Distance(vector3 &p1,vector3 &n,vector3 &p2)
{//Ra:obliczenie odległości punktu (p1) od płaszczyzny o wektorze normalnym (n) przechodzącej przez
(p2)
return n.x*(p1.x-p2.x)+n.y*(p1.y-p2.y)+n.z*(p1.z-p2.z); //ax1+by1+cz1+d, gdzie d=-(ax2+by2+cz2)
};
*/
bool TController::BackwardTrackBusy(TTrack *Track)
{ // najpierw sprawdzamy, czy na danym torze są pojazdy z innego składu
if (Track->iNumDynamics)
{ // jeśli tylko z własnego składu, to tor jest wolny
for (int i = 0; i < Track->iNumDynamics; ++i)
if (Track->Dynamics[i]->ctOwner != this) // jeśli jest jakiś cudzy
return true; // to tor jest zajęty i skanowanie nie obowiązuje
}
return false; // wolny
};
TEvent *__fastcall TController::CheckTrackEventBackward(double fDirection, TTrack *Track)
{ // sprawdzanie eventu w torze, czy jest sygnałowym - skanowanie do tyłu
TEvent *e = (fDirection > 0) ? Track->evEvent2 : Track->evEvent1;
if (e)
if (!e->bEnabled) // jeśli sygnałowy (nie dodawany do kolejki)
if (e->Type == tp_GetValues) // PutValues nie może się zmienić
return e;
return NULL;
};
TTrack *__fastcall TController::BackwardTraceRoute(double &fDistance, double &fDirection,
TTrack *Track, TEvent *&Event)
{ // szukanie sygnalizatora w kierunku przeciwnym jazdy (eventu odczytu komórki pamięci)
TTrack *pTrackChVel = Track; // tor ze zmianą prędkości
TTrack *pTrackFrom; // odcinek poprzedni, do znajdywania końca dróg
double fDistChVel = -1; // odległość do toru ze zmianą prędkości
double fCurrentDistance = pVehicle->RaTranslationGet(); // aktualna pozycja na torze
double s = 0;
if (fDirection > 0) // jeśli w kierunku Point2 toru
fCurrentDistance = Track->Length() - fCurrentDistance;
if (BackwardTrackBusy(Track))
{ // jak tor zajęty innym składem, to nie ma po co skanować
fDistance = 0; // to na tym torze stoimy
return NULL; // stop, skanowanie nie dało sensownych rezultatów
}
if ((Event = CheckTrackEventBackward(fDirection, Track)) != NULL)
{ // jeśli jest semafor na tym torze
fDistance = 0; // to na tym torze stoimy
return Track;
}
if ((Track->VelocityGet() == 0.0) || (Track->iDamageFlag & 128))
{ // jak prędkosć 0 albo uszkadza, to nie ma po co skanować
fDistance = 0; // to na tym torze stoimy
return NULL; // stop, skanowanie nie dało sensownych rezultatów
}
while (s < fDistance)
{
// Track->ScannedFlag=true; //do pokazywania przeskanowanych torów
pTrackFrom = Track; // zapamiętanie aktualnego odcinka
s += fCurrentDistance; // doliczenie kolejnego odcinka do przeskanowanej długości
if (fDirection > 0)
{ // jeśli szukanie od Point1 w kierunku Point2
if (Track->iNextDirection)
fDirection = -fDirection;
Track = Track->CurrentNext(); // może być NULL
}
else // if (fDirection<0)
{ // jeśli szukanie od Point2 w kierunku Point1
if (!Track->iPrevDirection)
fDirection = -fDirection;
Track = Track->CurrentPrev(); // może być NULL
}
if (Track == pTrackFrom)
Track = NULL; // koniec, tak jak dla torów
if (Track ?
(Track->VelocityGet() == 0.0) || (Track->iDamageFlag & 128) ||
BackwardTrackBusy(Track) :
true)
{ // gdy dalej toru nie ma albo zerowa prędkość, albo uszkadza pojazd
fDistance = s;
return NULL; // zwraca NULL, że skanowanie nie dało sensownych rezultatów
}
fCurrentDistance = Track->Length();
if ((Event = CheckTrackEventBackward(fDirection, Track)) != NULL)
{ // znaleziony tor z eventem
fDistance = s;
return Track;
}
}
Event = NULL; // jak dojdzie tu, to nie ma semafora
if (fDistChVel < 0)
{ // zwraca ostatni sprawdzony tor
fDistance = s;
return Track;
}
fDistance = fDistChVel; // odległość do zmiany prędkości
return pTrackChVel; // i tor na którym się zmienia
}
// sprawdzanie zdarzeń semaforów i ograniczeń szlakowych
void TController::SetProximityVelocity(double dist, double vel, const vector3 *pos)
{ // Ra:przeslanie do AI prędkości
/*
//!!!! zastąpić prawidłową reakcją AI na SetProximityVelocity !!!!
if (vel==0)
{//jeśli zatrzymanie, to zmniejszamy dystans o 10m
dist-=10.0;
};
if (dist<0.0) dist=0.0;
if ((vel<0)?true:dist>0.1*(MoverParameters->Vel*MoverParameters->Vel-vel*vel)+50)
{//jeśli jest dalej od umownej drogi hamowania
*/
PutCommand("SetProximityVelocity", dist, vel, pos);
/*
}
else
{//jeśli jest zagrożenie, że przekroczy
Mechanik->SetVelocity(floor(0.2*sqrt(dist)+vel),vel,stopError);
}
*/
}
TCommandType TController::BackwardScan()
{ // sprawdzanie zdarzeń semaforów z tyłu pojazdu, zwraca komendę
// dzięki temu będzie można stawać za wskazanym sygnalizatorem, a zwłaszcza jeśli będzie jazda
// na kozioł
// ograniczenia prędkości nie są wtedy istotne, również koniec toru jest do niczego nie
// przydatny
// zwraca true, jeśli należy odwrócić kierunek jazdy pojazdu
if ((OrderList[OrderPos] & ~(Shunt | Connect)))
return cm_Unknown; // skanowanie sygnałów tylko gdy jedzie w trybie manewrowym albo czeka na
// rozkazy
vector3 sl;
int startdir =
-pVehicles[0]->DirectionGet(); // kierunek jazdy względem sprzęgów pojazdu na czele
if (startdir == 0) // jeśli kabina i kierunek nie jest określony
return cm_Unknown; // nie robimy nic
double scandir =
startdir * pVehicles[0]->RaDirectionGet(); // szukamy od pierwszej osi w wybranym kierunku
if (scandir !=
0.0) // skanowanie toru w poszukiwaniu eventów GetValues (PutValues nie są przydatne)
{ // Ra: przy wstecznym skanowaniu prędkość nie ma znaczenia
// scanback=pVehicles[1]->NextDistance(fLength+1000.0); //odległość do następnego pojazdu,
// 1000 gdy nic nie ma
double scanmax = 1000; // 1000m do tyłu, żeby widział przeciwny koniec stacji
double scandist = scanmax; // zmodyfikuje na rzeczywiście przeskanowane
TEvent *e = NULL; // event potencjalnie od semafora
// opcjonalnie może być skanowanie od "wskaźnika" z przodu, np. W5, Tm=Ms1, koniec toru
TTrack *scantrack = BackwardTraceRoute(scandist, scandir, pVehicles[0]->RaTrackGet(),
e); // wg drugiej osi w kierunku ruchu
vector3 dir = startdir * pVehicles[0]->VectorFront(); // wektor w kierunku jazdy/szukania
if (!scantrack) // jeśli wstecz wykryto koniec toru
return cm_Unknown; // to raczej nic się nie da w takiej sytuacji zrobić
else
{ // a jeśli są dalej tory
double vmechmax; // prędkość ustawiona semaforem
if (e)
{ // jeśli jest jakiś sygnał na widoku
#if LOGBACKSCAN
AnsiString edir =
pVehicle->asName + " - " + AnsiString((scandir > 0) ? "Event2 " : "Event1 ");
#endif
// najpierw sprawdzamy, czy semafor czy inny znak został przejechany
vector3 pos = pVehicles[1]->RearPosition(); // pozycja tyłu
vector3 sem; // wektor do sygnału
if (e->Type == tp_GetValues)
{ // przesłać info o zbliżającym się semaforze
#if LOGBACKSCAN
edir += "(" + (e->Params[8].asGroundNode->asName) + "): ";
#endif
sl = e->PositionGet(); // położenie komórki pamięci
sem = sl - pos; // wektor do komórki pamięci od końca składu
// sem=e->Params[8].asGroundNode->pCenter-pos; //wektor do komórki pamięci
if (dir.x * sem.x + dir.z * sem.z < 0) // jeśli został minięty
// if ((mvOccupied->CategoryFlag&1)?(VelNext!=0.0):true) //dla pociągu wymagany
// sygnał zezwalający
{ // iloczyn skalarny jest ujemny, gdy sygnał stoi z tyłu
#if LOGBACKSCAN
WriteLog(edir + "- ignored as not passed yet");
#endif
return cm_Unknown; // nic
}
vmechmax = e->ValueGet(1); // prędkość przy tym semaforze
// przeliczamy odległość od semafora - potrzebne by były współrzędne początku
// składu
// scandist=(pos-e->Params[8].asGroundNode->pCenter).Length()-0.5*mvOccupied->Dim.L-10;
// //10m luzu
scandist = sem.Length() - 2; // 2m luzu przy manewrach wystarczy
if (scandist < 0)
scandist = 0; // ujemnych nie ma po co wysyłać
bool move = false; // czy AI w trybie manewerowym ma dociągnąć pod S1
if (e->Command() == cm_SetVelocity)
if ((vmechmax == 0.0) ? (OrderCurrentGet() & (Shunt | Connect)) :
(OrderCurrentGet() &
Connect)) // przy podczepianiu ignorować wyjazd?
move = true; // AI w trybie manewerowym ma dociągnąć pod S1
else
{ //
if ((scandist > fMinProximityDist) ?
(mvOccupied->Vel > 0.0) && (OrderCurrentGet() != Shunt) :
false)
{ // jeśli semafor jest daleko, a pojazd jedzie, to informujemy o
// zmianie prędkości
// jeśli jedzie manewrowo, musi dostać SetVelocity, żeby sie na pociągowy przełączył
// Mechanik->PutCommand("SetProximityVelocity",scandist,vmechmax,sl);
#if LOGBACKSCAN
// WriteLog(edir+"SetProximityVelocity "+AnsiString(scandist)+"
// "+AnsiString(vmechmax));
WriteLog(edir);
#endif
// SetProximityVelocity(scandist,vmechmax,&sl);
return (vmechmax > 0) ? cm_SetVelocity : cm_Unknown;
}
else // ustawiamy prędkość tylko wtedy, gdy ma ruszyć, stanąć albo ma
// stać
// if ((MoverParameters->Vel==0.0)||(vmechmax==0.0)) //jeśli stoi lub ma
// stanąć/stać
{ // semafor na tym torze albo lokomtywa stoi, a ma ruszyć, albo ma
// stanąć, albo nie ruszać
// stop trzeba powtarzać, bo inaczej zatrąbi i pojedzie sam
// PutCommand("SetVelocity",vmechmax,e->Params[9].asMemCell->Value2(),&sl,stopSem);
#if LOGBACKSCAN
WriteLog(edir + "SetVelocity " + AnsiString(vmechmax) + " " +
AnsiString(e->Params[9].asMemCell->Value2()));
#endif
return (vmechmax > 0) ? cm_SetVelocity : cm_Unknown;
}
}
if (OrderCurrentGet() ? OrderCurrentGet() & (Shunt | Connect) :
true) // w Wait_for_orders też widzi tarcze
{ // reakcja AI w trybie manewrowym dodatkowo na sygnały manewrowe
if (move ? true : e->Command() == cm_ShuntVelocity)
{ // jeśli powyżej było SetVelocity 0 0, to dociągamy pod S1
if ((scandist > fMinProximityDist) ?
(mvOccupied->Vel > 0.0) || (vmechmax == 0.0) :
false)
{ // jeśli tarcza jest daleko, to:
//- jesli pojazd jedzie, to informujemy o zmianie prędkości
//- jeśli stoi, to z własnej inicjatywy może podjechać pod zamkniętą
// tarczę
if (mvOccupied->Vel > 0.0) // tylko jeśli jedzie
{ // Mechanik->PutCommand("SetProximityVelocity",scandist,vmechmax,sl);
#if LOGBACKSCAN
// WriteLog(edir+"SetProximityVelocity "+AnsiString(scandist)+"
// "+AnsiString(vmechmax));
WriteLog(edir);
#endif
// SetProximityVelocity(scandist,vmechmax,&sl);
return (iDrivigFlags & moveTrackEnd) ?
cm_ChangeDirection :
cm_Unknown; // jeśli jedzie na W5 albo koniec toru,
// to można zmienić kierunek
}
}
else // ustawiamy prędkość tylko wtedy, gdy ma ruszyć, albo stanąć albo
// ma stać pod tarczą
{ // stop trzeba powtarzać, bo inaczej zatrąbi i pojedzie sam
// if ((MoverParameters->Vel==0.0)||(vmechmax==0.0)) //jeśli jedzie
// lub ma stanąć/stać
{ // nie dostanie komendy jeśli jedzie i ma jechać
// PutCommand("ShuntVelocity",vmechmax,e->Params[9].asMemCell->Value2(),&sl,stopSem);
#if LOGBACKSCAN
WriteLog(edir + "ShuntVelocity " + AnsiString(vmechmax) + " " +
AnsiString(e->ValueGet(2)));
#endif
return (vmechmax > 0) ? cm_ShuntVelocity : cm_Unknown;
}
}
if ((vmechmax != 0.0) && (scandist < 100.0))
{ // jeśli Tm w odległości do 100m podaje zezwolenie na jazdę, to od
// razu ją ignorujemy, aby móc szukać kolejnej
// eSignSkip=e; //wtedy uznajemy ignorowaną przy poszukiwaniu nowej
#if LOGBACKSCAN
WriteLog(edir + "- will be ignored due to Ms2");
#endif
return (vmechmax > 0) ? cm_ShuntVelocity : cm_Unknown;
}
} // if (move?...
} // if (OrderCurrentGet()==Shunt)
if (!e->bEnabled) // jeśli skanowany
if (e->StopCommand()) // a podłączona komórka ma komendę
return cm_Command; // to też się obrócić
} // if (e->Type==tp_GetValues)
} // if (e)
} // if (scantrack)
} // if (scandir!=0.0)
return cm_Unknown; // nic
};
AnsiString TController::NextStop()
{ // informacja o następnym zatrzymaniu, wyświetlane pod [F1]
if (asNextStop.Length() < 20)
return ""; // nie zawiera nazwy stacji, gdy dojechał do końca
// dodać godzinę odjazdu
if (!TrainParams)
return ""; // tu nie powinno nigdy wejść
TMTableLine *t = TrainParams->TimeTable + TrainParams->StationIndex;
if (t->Dh >= 0) // jeśli jest godzina odjazdu
return asNextStop.SubString(20, 30) + AnsiString(" ") + AnsiString(int(t->Dh)) +
AnsiString(":") + AnsiString(int(100 + t->Dm)).SubString(2, 2); // odjazd
else if (t->Ah >= 0) // przyjazd
return asNextStop.SubString(20, 30) + AnsiString(" (") + AnsiString(int(t->Ah)) +
AnsiString(":") + AnsiString(int(100 + t->Am)).SubString(2, 2) +
AnsiString(")"); // przyjazd
return "";
};
//-----------koniec skanowania semaforow
void TController::TakeControl(bool yes)
{ // przejęcie kontroli przez AI albo oddanie
if (AIControllFlag == yes)
return; // już jest jak ma być
if (yes) //żeby nie wykonywać dwa razy
{ // teraz AI prowadzi
AIControllFlag = AIdriver;
pVehicle->Controller = AIdriver;
iDirection = 0; // kierunek jazdy trzeba dopiero zgadnąć
// gdy zgaszone światła, flaga podjeżdżania pod semafory pozostaje bez zmiany
if (OrderCurrentGet()) // jeśli coś robi
PrepareEngine(); // niech sprawdzi stan silnika
else // jeśli nic nie robi
if (pVehicle->iLights[mvOccupied->CabNo < 0 ? 1 : 0] &
21) // któreś ze świateł zapalone?
{ // od wersji 357 oczekujemy podania komend dla AI przez scenerię
OrderNext(Prepare_engine);
if (pVehicle->iLights[mvOccupied->CabNo < 0 ? 1 : 0] & 4) // górne światło zapalone
OrderNext(Obey_train); // jazda pociągowa
else
OrderNext(Shunt); // jazda manewrowa
if (mvOccupied->Vel >= 1.0) // jeśli jedzie (dla 0.1 ma stać)
iDrivigFlags &= ~moveStopHere; // to ma nie czekać na sygnał, tylko jechać
else
iDrivigFlags |= moveStopHere; // a jak stoi, to niech czeka
}
/* od wersji 357 oczekujemy podania komend dla AI przez scenerię
if (OrderCurrentGet())
{if (OrderCurrentGet()<Shunt)
{OrderNext(Prepare_engine);
if (pVehicle->iLights[mvOccupied->CabNo<0?1:0]&4) //górne światło
OrderNext(Obey_train); //jazda pociągowa
else
OrderNext(Shunt); //jazda manewrowa
}
}
else //jeśli jest w stanie Wait_for_orders
JumpToFirstOrder(); //uruchomienie?
// czy dac ponizsze? to problematyczne
//SetVelocity(pVehicle->GetVelocity(),-1); //utrzymanie dotychczasowej?
if (pVehicle->GetVelocity()>0.0)
SetVelocity(-1,-1); //AI ustali sobie odpowiednią prędkość
*/
// Activation(); //przeniesie użytkownika w ostatnio wybranym kierunku
CheckVehicles(); // ustawienie świateł
TableClear(); // ponowne utworzenie tabelki, bo człowiek mógł pojechać niezgodnie z
// sygnałami
}
else
{ // a teraz użytkownik
AIControllFlag = Humandriver;
pVehicle->Controller = Humandriver;
}
};
void TController::DirectionForward(bool forward)
{ // ustawienie jazdy do przodu dla true i do tyłu dla false (zależy od kabiny)
while (mvControlling->MainCtrlPos) // samo zapętlenie DecSpeed() nie wystarcza
DecSpeed(true); // wymuszenie zerowania nastawnika jazdy, inaczej się może zawiesić
if (forward)
while (mvOccupied->ActiveDir <= 0)
mvOccupied->DirectionForward(); // do przodu w obecnej kabinie
else
while (mvOccupied->ActiveDir >= 0)
mvOccupied->DirectionBackward(); // do tyłu w obecnej kabinie
if (mvOccupied->EngineType == DieselEngine) // specjalnie dla SN61
if (iDrivigFlags & moveActive) // jeśli był już odpalony
if (mvControlling->RList[mvControlling->MainCtrlPos].Mn == 0)
mvControlling->IncMainCtrl(1); //żeby nie zgasł
};
AnsiString TController::Relation()
{
return TrainParams->ShowRelation();
}; // zwraca relację pociągu
AnsiString TController::TrainName()
{
return TrainParams->TrainName;
}; // zwraca relację pociągu
int TController::StationCount()
{
return TrainParams->StationCount;
}; // zwraca ilość stacji (miejsc zatrzymania)
int TController::StationIndex()
{
return TrainParams->StationIndex;
}; // zwraca indeks aktualnej stacji (miejsca zatrzymania)
bool TController::IsStop()
{
return TrainParams->IsStop();
}; // informuje, czy jest zatrzymanie na najbliższej stacji
void TController::MoveTo(TDynamicObject *to)
{ // przesunięcie AI do innego pojazdu (przy zmianie kabiny)
// mvOccupied->CabDeactivisation(); //wyłączenie kabiny w opuszczanym
pVehicle->Mechanik = to->Mechanik; //żeby się zamieniły, jak jest jakieś drugie
pVehicle = to;
ControllingSet(); // utworzenie połączenia do sterowanego pojazdu
pVehicle->Mechanik = this;
// iDirection=0; //kierunek jazdy trzeba dopiero zgadnąć
};
void TController::ControllingSet()
{ // znajduje człon do sterowania w EZT będzie to silnikowy
// problematyczne jest sterowanie z członu biernego, dlatego damy AI silnikowy
// dzięki temu będzie wirtualna kabina w silnikowym, działająca w rozrządczym
// w plikach FIZ zostały zgubione ujemne maski sprzęgów, stąd problemy z EZT
mvOccupied = pVehicle->MoverParameters; // domyślny skrót do obiektu parametrów
mvControlling = pVehicle->ControlledFind()->MoverParameters; // poszukiwanie członu sterowanego
};
AnsiString TController::TableText(int i)
{ // pozycja tabelki prędkości
i = (iFirst + i) % iSpeedTableSize; // numer pozycji
if (i != iLast) // w (iLast) znajduje się kolejny tor do przeskanowania, ale nie jest ona
// aktywną
return sSpeedTable[i].TableText();
return ""; // wskaźnik końca
};
int TController::CrossRoute(TTrack *tr)
{ // zwraca numer segmentu dla skrzyżowania (tr)
// pożądany numer segmentu jest określany podczas skanowania drogi
// droga powinna być określona sposobem przejazdu przez skrzyżowania albo współrzędnymi miejsca
// docelowego
for (int i = iFirst; i != iLast; i = (i + 1) % iSpeedTableSize)
{ // trzeba przejrzeć tabelę skanowania w poszukiwaniu (tr)
// i jak się znajdzie, to zwrócić zapamiętany numer segmentu i kierunek przejazdu
// (-6..-1,1..6)
if ((sSpeedTable[i].iFlags & 3) == 3) // jeśli pozycja istotna (1) oraz odcinek (2)
if (sSpeedTable[i].trTrack == tr) // jeśli pozycja odpowiadająca skrzyżowaniu (tr)
return (sSpeedTable[i].iFlags >> 28); // najstarsze 4 bity jako liczba -8..7
}
return 0; // nic nie znaleziono?
};
void TController::RouteSwitch(int d)
{ // ustawienie kierunku jazdy z kabiny
d &= 3;
if (d)
if (iRouteWanted != d)
{ // nowy kierunek
iRouteWanted = d; // zapamiętanie
if (mvOccupied->CategoryFlag & 2) // jeśli samochód
for (int i = iFirst; i != iLast; i = (i + 1) % iSpeedTableSize)
{ // szukanie pierwszego skrzyżowania i resetowanie kierunku na nim
if ((sSpeedTable[i].iFlags & 3) ==
3) // jeśli pozycja istotna (1) oraz odcinek (2)
if ((sSpeedTable[i].iFlags & 32) == 0) // odcinek nie może być miniętym
if (sSpeedTable[i].trTrack->eType == tt_Cross) // jeśli skrzyżowanie
{ // obcięcie tabelki skanowania przed skrzyżowaniem, aby ponownie
// wybrać drogę
iLast = i - 1; // ponowne skanowanie skrzyżowania (w zwrotnicach
// jest iLast=i, ale tam jest prościej)
if (iLast < 0)
iLast += iSpeedTableSize; // bo tabelka jest zapętlona
return;
}
}
}
};
AnsiString TController::OwnerName()
{
return pVehicle ? pVehicle->MoverParameters->Name : AnsiString("none");
};