mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-03-22 15:05:03 +01:00
6823 lines
331 KiB
C++
6823 lines
331 KiB
C++
/*
|
||
This Source Code Form is subject to the
|
||
terms of the Mozilla Public License, v.
|
||
2.0. If a copy of the MPL was not
|
||
distributed with this file, You can
|
||
obtain one at
|
||
http://mozilla.org/MPL/2.0/.
|
||
*/
|
||
/*
|
||
MaSzyna EU07 locomotive simulator
|
||
Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others
|
||
|
||
*/
|
||
|
||
#include "stdafx.h"
|
||
#include "Driver.h"
|
||
|
||
//#include <direct.h>
|
||
#include "Globals.h"
|
||
#include "translation.h"
|
||
#include "Logs.h"
|
||
#include "Train.h"
|
||
#include "mtable.h"
|
||
#include "DynObj.h"
|
||
#include "Event.h"
|
||
#include "MemCell.h"
|
||
#include "simulation.h"
|
||
#include "simulationtime.h"
|
||
#include "Track.h"
|
||
#include "station.h"
|
||
#include "keyboardinput.h"
|
||
#include "utilities.h"
|
||
|
||
#define LOGVELOCITY 0
|
||
#define LOGORDERS 1
|
||
#define LOGSTOPS 1
|
||
#define LOGBACKSCAN 0
|
||
#define LOGPRESS 0
|
||
|
||
// finds point of specified track nearest to specified event. returns: distance to that point from the specified end of the track
|
||
// TODO: move this to file with all generic routines, too easy to forget it's here and it may come useful
|
||
double
|
||
ProjectEventOnTrack( basic_event const *Event, TTrack const *Track, double const Direction ) {
|
||
|
||
auto const segment = Track->CurrentSegment();
|
||
auto const nearestpoint = segment->find_nearest_point( Event->input_location() );
|
||
return (
|
||
Direction > 0 ?
|
||
nearestpoint * segment->GetLength() : // measure from point1
|
||
( 1.0 - nearestpoint ) * segment->GetLength() ); // measure from point2
|
||
};
|
||
|
||
double GetDistanceToEvent(TTrack const *track, basic_event const *event, double scan_dir, double start_dist, int iter = 0, bool back = false)
|
||
{
|
||
if( track == nullptr ) { return start_dist; }
|
||
|
||
auto const segment = track->CurrentSegment();
|
||
auto const pos_event = event->input_location();
|
||
double len1, len2;
|
||
double sd = scan_dir;
|
||
double seg_len = scan_dir > 0 ? 0.0 : 1.0;
|
||
double const dzielnik = 1.0 / segment->GetLength();// rozdzielczosc mniej wiecej 1m
|
||
int krok = 0; // krok obliczeniowy do sprawdzania czy odwracamy
|
||
len2 = (pos_event - segment->FastGetPoint(seg_len)).LengthSquared();
|
||
do
|
||
{
|
||
len1 = len2;
|
||
seg_len += scan_dir > 0 ? dzielnik : -dzielnik;
|
||
len2 = (pos_event - segment->FastGetPoint(seg_len)).LengthSquared();
|
||
++krok;
|
||
} while ((len1 > len2) && (seg_len >= dzielnik && (seg_len <= (1.0 - dzielnik))));
|
||
//trzeba sprawdzić czy seg_len nie osiągnął skrajnych wartości, bo wtedy
|
||
// trzeba sprawdzić tor obok
|
||
if (1 == krok)
|
||
sd = -sd; // jeśli tylko jeden krok tzn, że event przy poprzednim sprawdzaym torze
|
||
if (((1 == krok) || (seg_len <= dzielnik) || (seg_len > (1.0 - dzielnik))) && (iter < 3))
|
||
{ // przejście na inny tor
|
||
track = track->Connected(int(sd), sd);
|
||
start_dist += (1 == krok) ? 0 : back ? -segment->GetLength() : segment->GetLength();
|
||
if( ( track != nullptr )
|
||
&& ( track->eType == tt_Cross ) ) {
|
||
// NOTE: tracing through crossroads currently poses risk of tracing through wrong segment
|
||
// as it's possible to be performerd before setting a route through the crossroads
|
||
// as a stop-gap measure we don't trace through crossroads which should be reasonable in most cases
|
||
// TODO: establish route before the scan, or a way for the function to know which route to pick
|
||
return start_dist;
|
||
}
|
||
else {
|
||
return GetDistanceToEvent( track, event, sd, start_dist, ++iter, 1 == krok ? true : false );
|
||
}
|
||
}
|
||
else
|
||
{ // obliczenie mojego toru
|
||
seg_len -= scan_dir > 0 ? dzielnik : -dzielnik; //trzeba wrócić do pozycji len1
|
||
seg_len = scan_dir < 0 ? 1 - seg_len : seg_len;
|
||
seg_len = back ? 1 - seg_len : seg_len; // odwracamy jeśli idzie do tyłu
|
||
start_dist -= back ? segment->GetLength() : 0;
|
||
return start_dist + (segment->GetLength() * seg_len);
|
||
}
|
||
};
|
||
|
||
/*
|
||
|
||
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!
|
||
// 20. wybór pozycji zaworu maszynisty w oparciu o zadane opoznienie hamowania
|
||
|
||
|
||
// 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.85; //[m/ss]
|
||
const double HardAcceleration = 9.81;
|
||
const double PrepareTime = 2.0; //[s] przebłyski świadomości przy odpalaniu
|
||
bool WriteLogFlag = false;
|
||
double const deltalog = 0.05; // przyrost czasu
|
||
|
||
std::string StopReasonTable[] = {
|
||
// przyczyny zatrzymania ruchu AI
|
||
"", // 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
|
||
};
|
||
|
||
//---------------------------------------------------------------------------
|
||
//---------------------------------------------------------------------------
|
||
//---------------------------------------------------------------------------
|
||
|
||
TSpeedPos::TSpeedPos(TTrack *track, double dist, int flag)
|
||
{
|
||
Set(track, dist, flag);
|
||
};
|
||
|
||
TSpeedPos::TSpeedPos(basic_event *event, double dist, double length, TOrders order)
|
||
{
|
||
Set(event, dist, length, order);
|
||
};
|
||
|
||
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 = Math3D::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->input_command();
|
||
double value1 = evEvent->input_value(1);
|
||
double value2 = evEvent->input_value(2);
|
||
switch (command)
|
||
{
|
||
case TCommandType::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 TCommandType::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 TCommandType::cm_SectionVelocity:
|
||
// odcinek z ograniczeniem prędkości
|
||
fVelNext = value1;
|
||
fSectionVelocityDist = value2;
|
||
iFlags |= spSectionVel;
|
||
break;
|
||
case TCommandType::cm_RoadVelocity:
|
||
// prędkość drogowa (od tej pory będzie jako domyślna najwyższa)
|
||
fVelNext = value1;
|
||
iFlags |= spRoadVel;
|
||
break;
|
||
case TCommandType::cm_PassengerStopPoint:
|
||
// nie ma dostępu do rozkładu
|
||
// przystanek, najwyżej AI zignoruje przy analizie tabelki
|
||
fVelNext = 0.0;
|
||
iFlags |= spPassengerStopPoint; // niestety nie da się w tym miejscu współpracować z rozkładem
|
||
/*
|
||
// NOTE: not used for now as it might be unnecessary
|
||
// special case, potentially override any set speed limits if requested
|
||
// NOTE: we test it here because for the time being it's only used for passenger stops
|
||
if( TestFlag( iFlags, spDontApplySpeedLimit ) ) {
|
||
fVelNext = -1;
|
||
}
|
||
*/
|
||
break;
|
||
case TCommandType::cm_SetProximityVelocity:
|
||
// musi zostać gdyż inaczej nie działają manewry
|
||
fVelNext = -1;
|
||
iFlags |= spProximityVelocity;
|
||
// fSectionVelocityDist = value2;
|
||
break;
|
||
case TCommandType::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
|
||
// nie manewrowa, nie przystanek, nie zatrzymać na SBL
|
||
iFlags &= ~(spShuntSemaphor | spPassengerStopPoint | spStopOnSBL);
|
||
// jak nieznana komenda w komórce sygnałowej, to zatrzymujemy
|
||
fVelNext = 0.0;
|
||
}
|
||
};
|
||
|
||
bool TSpeedPos::Update()
|
||
{
|
||
if( fDist < 0.0 ) {
|
||
// trzeba zazanaczyć, że minięty
|
||
iFlags |= spElapsed;
|
||
}
|
||
if (iFlags & spTrack) {
|
||
// road/track
|
||
if (trTrack) {
|
||
// może być NULL, jeśli koniec toru (???)
|
||
fVelNext = trTrack->VelocityGet(); // aktualizacja prędkości (może być zmieniana eventem)
|
||
|
||
if( trTrack->iCategoryFlag & 1 ) {
|
||
// railways
|
||
if( iFlags & spSwitch ) {
|
||
// jeśli odcinek zmienny
|
||
if( ( ( trTrack->GetSwitchState() & 1 ) != 0 ) !=
|
||
( ( iFlags & spSwitchStatus ) != 0 ) ) {
|
||
// czy stan się zmienił?
|
||
// Ra: zakładam, że są tylko 2 możliwe stany
|
||
iFlags ^= spSwitchStatus;
|
||
if( ( iFlags & spElapsed ) == 0 ) {
|
||
// 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
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
// roads and others
|
||
if( iFlags & 0xF0000000 ) {
|
||
// jeśli skrzyżowanie, ograniczyć prędkość przy skręcaniu
|
||
if( ( iFlags & 0xF0000000 ) > 0x10000000 ) {
|
||
// ±1 to jazda na wprost, ±2 nieby też, ale z przecięciem głównej drogi - chyba że jest równorzędne...
|
||
// TODO: uzależnić prędkość od promienia;
|
||
// albo niech będzie ograniczona w skrzyżowaniu (velocity z ujemną wartością)
|
||
fVelNext = 30.0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if (iFlags & spEvent) {
|
||
// jeśli event
|
||
if( ( ( iFlags & spElapsed ) == 0 )
|
||
|| ( fVelNext == 0.0 ) ) {
|
||
// ignore already passed signals, but keep an eye on overrun stops
|
||
// 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;
|
||
};
|
||
|
||
std::string TSpeedPos::GetName() const
|
||
{
|
||
if (iFlags & spTrack) // jeśli tor
|
||
return trTrack->name();
|
||
else if( iFlags & spEvent ) // jeśli event
|
||
return evEvent->m_name;
|
||
else
|
||
return "";
|
||
}
|
||
|
||
std::string TSpeedPos::TableText() const
|
||
{ // pozycja tabelki pr?dko?ci
|
||
if (iFlags & spEnabled)
|
||
{ // o ile pozycja istotna
|
||
return to_hex_str(iFlags, 8) + " " + to_string(fDist, 1, 6) +
|
||
" " + (fVelNext == -1.0 ? " -" : to_string(static_cast<int>(fVelNext), 0, 3)) + " " + GetName();
|
||
}
|
||
return "Empty";
|
||
}
|
||
|
||
bool TSpeedPos::IsProperSemaphor(TOrders order)
|
||
{ // sprawdzenie czy semafor jest zgodny z trybem jazdy
|
||
if (order < Obey_train) // Wait_for_orders, Prepare_engine, Change_direction, Connect, Disconnect, Shunt
|
||
{
|
||
if (iFlags & (spSemaphor | spShuntSemaphor))
|
||
return true;
|
||
else if (iFlags & spOutsideStation)
|
||
return true;
|
||
}
|
||
else if (order & Obey_train)
|
||
{
|
||
if (iFlags & spSemaphor)
|
||
return true;
|
||
}
|
||
return false; // true gdy zatrzymanie, wtedy nie ma po co skanować dalej
|
||
}
|
||
|
||
bool TSpeedPos::Set(basic_event *event, double dist, double length, TOrders order)
|
||
{ // zapamiętanie zdarzenia
|
||
fDist = dist;
|
||
iFlags = spEvent;
|
||
evEvent = event;
|
||
vPos = event->input_location(); // współrzędne eventu albo komórki pamięci (zrzutować na tor?)
|
||
// ignore events located behind the consist, but with exception of stop points which may be needed to update freshly received timetable
|
||
if( ( dist + length < 0 )
|
||
&& ( event->input_command() != TCommandType::cm_PassengerStopPoint ) ) {
|
||
return false;
|
||
}
|
||
iFlags |= spEnabled;
|
||
CommandCheck(); // sprawdzenie typu komendy w evencie i określenie prędkości
|
||
// zależnie od trybu sprawdzenie czy jest tutaj gdzieś semafor lub tarcza manewrowa
|
||
// jeśli wskazuje stop wtedy wystawiamy true jako koniec sprawdzania
|
||
// WriteLog("EventSet: Vel=" + AnsiString(fVelNext) + " iFlags=" + AnsiString(iFlags) + " order="+AnsiString(order));
|
||
if (order < Obey_train) // Wait_for_orders, Prepare_engine, Change_direction, Connect, Disconnect, Shunt
|
||
{
|
||
if (iFlags & (spSemaphor | spShuntSemaphor) && fVelNext == 0.0)
|
||
return true;
|
||
else if (iFlags & spOutsideStation)
|
||
return true;
|
||
}
|
||
else if (order & Obey_train)
|
||
{
|
||
if (iFlags & spSemaphor && fVelNext == 0.0)
|
||
return true;
|
||
}
|
||
return false; // 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 ? spTrack : (spTrack | spSwitch) ); // 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 =
|
||
( iFlags & spReverse ) ?
|
||
trTrack->CurrentSegment()->FastGetPoint_1() :
|
||
trTrack->CurrentSegment()->FastGetPoint_0();
|
||
}
|
||
};
|
||
|
||
//---------------------------------------------------------------------------
|
||
|
||
void TController::TableClear()
|
||
{ // wyczyszczenie tablicy
|
||
sSpeedTable.clear();
|
||
iLast = -1;
|
||
iTableDirection = 0; // nieznany
|
||
tLast = nullptr;
|
||
fLastVel = -1.0;
|
||
SemNextIndex = -1;
|
||
SemNextStopIndex = -1;
|
||
eSignSkip = nullptr; // nic nie pomijamy
|
||
};
|
||
|
||
std::vector<basic_event *> TController::CheckTrackEvent( TTrack *Track, double const fDirection ) const
|
||
{ // sprawdzanie eventów na podanym torze do podstawowego skanowania
|
||
std::vector<basic_event *> events;
|
||
auto const &eventsequence { ( fDirection > 0 ? Track->m_events2 : Track->m_events1 ) };
|
||
for( auto const &event : eventsequence ) {
|
||
if( ( event.second != nullptr )
|
||
&& ( event.second->m_passive ) ) {
|
||
events.emplace_back( event.second );
|
||
}
|
||
}
|
||
return events;
|
||
}
|
||
|
||
bool TController::TableAddNew()
|
||
{ // zwiększenie użytej tabelki o jeden rekord
|
||
sSpeedTable.emplace_back(); // add a new slot
|
||
iLast = sSpeedTable.size() - 1;
|
||
return true; // false gdy się nałoży
|
||
};
|
||
|
||
bool TController::TableNotFound(basic_event const *Event, double const Distance ) const
|
||
{ // sprawdzenie, czy nie został już dodany do tabelki (np. podwójne W4 robi problemy)
|
||
auto lookup =
|
||
std::find_if(
|
||
sSpeedTable.begin(),
|
||
sSpeedTable.end(),
|
||
[Event]( TSpeedPos const &speedpoint ){
|
||
return ( ( true == TestFlag( speedpoint.iFlags, spEnabled | spEvent ) )
|
||
&& ( speedpoint.evEvent == Event ) ); } );
|
||
|
||
if( ( Global.iWriteLogEnabled & 8 )
|
||
&& ( lookup != sSpeedTable.end() ) ) {
|
||
WriteLog( "Speed table for " + OwnerName() + " already contains event " + lookup->evEvent->m_name );
|
||
}
|
||
|
||
// ignore duplicates which seem to be reasonably apart from each other, on account of looping tracks
|
||
// NOTE: since supplied distance is only rough approximation of distance to the event, we're using large safety margin
|
||
return (
|
||
( lookup == sSpeedTable.end() )
|
||
|| ( Distance - lookup->fDist > 100.0 ) );
|
||
};
|
||
|
||
void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle)
|
||
{ // skanowanie trajektorii na odległość (fDistance) od (pVehicle) w kierunku przodu składu i
|
||
// uzupełnianie tabelki
|
||
// WriteLog("Starting TableTraceRoute");
|
||
|
||
TTrack *pTrack{ nullptr }; // zaczynamy od ostatniego analizowanego toru
|
||
double fTrackLength{ 0.0 }; // długość aktualnego toru (krótsza dla pierwszego)
|
||
double fCurrentDistance{ 0.0 }; // aktualna przeskanowana długość
|
||
double fLastDir{ 0.0 };
|
||
|
||
if (iTableDirection != iDirection ) {
|
||
// jeśli zmiana kierunku, zaczynamy od toru ze wskazanym pojazdem
|
||
TableClear();
|
||
/*
|
||
// aktualna prędkość // changed to -1 to recognize speed limit, if any
|
||
fLastVel = -1.0;
|
||
sSpeedTable.clear();
|
||
iLast = -1;
|
||
tLast = nullptr; //żaden nie sprawdzony
|
||
SemNextIndex = -1;
|
||
SemNextStopIndex = -1;
|
||
*/
|
||
if( VelSignalLast == 0.0 ) {
|
||
// don't allow potential red light overrun keep us from reversing
|
||
VelSignalLast = -1.0;
|
||
}
|
||
iTableDirection = iDirection; // ustalenie w jakim kierunku jest wypełniana tabelka względem pojazdu
|
||
pTrack = pVehicle->RaTrackGet(); // odcinek, na którym stoi
|
||
fTrackLength = pVehicle->RaTranslationGet(); // pozycja na tym torze (odległość od Point1)
|
||
fLastDir = pVehicle->DirectionGet() * pVehicle->RaDirectionGet(); // ustalenie kierunku skanowania na torze
|
||
if( fLastDir < 0.0 ) {
|
||
// jeśli w kierunku Point2 toru
|
||
fTrackLength = pTrack->Length() - fTrackLength; // przeskanowana zostanie odległość do Point2
|
||
}
|
||
// account for the fact tracing begins from active axle, not the actual front of the vehicle
|
||
// NOTE: position of the couplers is modified by track offset, but the axles ain't, so we need to account for this as well
|
||
fTrackLength -= (
|
||
pVehicle->AxlePositionGet()
|
||
- pVehicle->RearPosition()
|
||
+ pVehicle->VectorLeft() * pVehicle->MoverParameters->OffsetTrackH )
|
||
.Length();
|
||
// aktualna odległość ma być ujemna gdyż jesteśmy na końcu składu
|
||
fCurrentDistance = -fLength - fTrackLength;
|
||
fTrackLength = pTrack->Length(); //skasowanie zmian w zmiennej żeby poprawnie liczyło w dalszych krokach
|
||
MoveDistanceReset(); // AI startuje 1s po zaczęciu jazdy i mógł już coś przejechać
|
||
}
|
||
else {
|
||
if( iTableDirection == 0 ) { return; }
|
||
// NOTE: provisory fix for BUG: sempahor indices no longer matching table size
|
||
// TODO: find and really fix the reason it happens
|
||
if( ( SemNextIndex != -1 )
|
||
&& ( SemNextIndex >= sSpeedTable.size() ) ) {
|
||
SemNextIndex = -1;
|
||
}
|
||
if( ( SemNextStopIndex != -1 )
|
||
&& ( SemNextStopIndex >= sSpeedTable.size() ) ) {
|
||
SemNextStopIndex = -1;
|
||
}
|
||
// kontynuacja skanowania od ostatnio sprawdzonego toru (w ostatniej pozycji zawsze jest tor)
|
||
if( ( SemNextStopIndex != -1 )
|
||
&& ( sSpeedTable[SemNextStopIndex].fVelNext == 0.0 ) ) {
|
||
// znaleziono semafor lub tarczę lub tor z prędkością zero, trzeba sprawdzić czy to nadał semafor
|
||
// jeśli jest następny semafor to sprawdzamy czy to on nadał zero
|
||
if( ( OrderCurrentGet() & Obey_train )
|
||
&& ( sSpeedTable[SemNextStopIndex].iFlags & spSemaphor ) ) {
|
||
return;
|
||
}
|
||
else {
|
||
if( ( OrderCurrentGet() < Obey_train )
|
||
&& ( sSpeedTable[SemNextStopIndex].iFlags & ( spSemaphor | spShuntSemaphor | spOutsideStation ) ) ) {
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
auto const &lastspeedpoint = sSpeedTable[ iLast ];
|
||
pTrack = lastspeedpoint.trTrack;
|
||
assert( pTrack != nullptr );
|
||
// flaga ustawiona, gdy Point2 toru jest blizej
|
||
fLastDir = ( ( ( lastspeedpoint.iFlags & spReverse ) != 0 ) ? -1.0 : 1.0 );
|
||
fCurrentDistance = lastspeedpoint.fDist; // aktualna odleglosc do jego Point1
|
||
fTrackLength = pTrack->Length();
|
||
}
|
||
|
||
if( iTableDirection == 0 ) {
|
||
// don't bother
|
||
return;
|
||
}
|
||
|
||
while (fCurrentDistance < fDistance)
|
||
{
|
||
if (pTrack != tLast) // ostatni zapisany w tabelce nie był jeszcze sprawdzony
|
||
{ // jeśli tor nie był jeszcze sprawdzany
|
||
if( Global.iWriteLogEnabled & 8 ) {
|
||
WriteLog( "Speed table for " + OwnerName() + " tracing through track " + pTrack->name() );
|
||
}
|
||
|
||
auto const events { CheckTrackEvent( pTrack, fLastDir ) };
|
||
for( auto *pEvent : events ) {
|
||
if( pEvent != nullptr ) // jeśli jest semafor na tym torze
|
||
{ // trzeba sprawdzić tabelkę, bo dodawanie drugi raz tego samego przystanku nie jest korzystne
|
||
if (TableNotFound(pEvent, fCurrentDistance)) // jeśli nie ma
|
||
{
|
||
TableAddNew(); // zawsze jest true
|
||
|
||
if (Global.iWriteLogEnabled & 8) {
|
||
WriteLog("Speed table for " + OwnerName() + " found new event, " + pEvent->m_name);
|
||
}
|
||
auto &newspeedpoint = sSpeedTable[iLast];
|
||
/*
|
||
if( newspeedpoint.Set(
|
||
pEvent,
|
||
fCurrentDistance + ProjectEventOnTrack( pEvent, pTrack, fLastDir ),
|
||
OrderCurrentGet() ) ) {
|
||
*/
|
||
if( newspeedpoint.Set(
|
||
pEvent,
|
||
GetDistanceToEvent( pTrack, pEvent, fLastDir, fCurrentDistance ),
|
||
fLength,
|
||
OrderCurrentGet() ) ) {
|
||
|
||
fDistance = newspeedpoint.fDist; // jeśli sygnał stop, to nie ma potrzeby dalej skanować
|
||
SemNextStopIndex = iLast;
|
||
if (SemNextIndex == -1) {
|
||
SemNextIndex = iLast;
|
||
}
|
||
if (Global.iWriteLogEnabled & 8) {
|
||
WriteLog("(stop signal from "
|
||
+ (SemNextStopIndex != -1 ? sSpeedTable[SemNextStopIndex].GetName() : "unknown semaphor")
|
||
+ ")");
|
||
}
|
||
}
|
||
else {
|
||
if( ( true == newspeedpoint.IsProperSemaphor( OrderCurrentGet() ) )
|
||
&& ( SemNextIndex == -1 ) ) {
|
||
SemNextIndex = iLast; // sprawdzamy czy pierwszy na drodze
|
||
}
|
||
if (Global.iWriteLogEnabled & 8) {
|
||
WriteLog("(forward signal for "
|
||
+ (SemNextIndex != -1 ? sSpeedTable[SemNextIndex].GetName() : "unknown semaphor")
|
||
+ ")");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} // 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
|
||
TableAddNew();
|
||
sSpeedTable[ iLast ].Set(
|
||
pTrack, fCurrentDistance,
|
||
( fLastDir < 0 ?
|
||
spEnabled | spReverse :
|
||
spEnabled ) ); // 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
|
||
int routewanted;
|
||
if( false == AIControllFlag ) {
|
||
routewanted = (
|
||
input::keys[ GLFW_KEY_LEFT ] != GLFW_RELEASE ? 1 :
|
||
input::keys[ GLFW_KEY_RIGHT ] != GLFW_RELEASE ? 2 :
|
||
3 );
|
||
}
|
||
else {
|
||
routewanted = 1 + std::floor( Random( static_cast<double>( pTrack->RouteCount() ) - 0.001 ) );
|
||
}
|
||
|
||
sSpeedTable[iLast].iFlags |=
|
||
( ( pTrack->CrossSegment(
|
||
(fLastDir < 0 ?
|
||
tLast->iPrevDirection :
|
||
tLast->iNextDirection),
|
||
/*
|
||
iRouteWanted )
|
||
*/
|
||
routewanted )
|
||
& 0xf ) << 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 != nullptr )
|
||
&& ( tLast->fRadius != 0.0 ) )) // koniec łuku też jest istotny
|
||
{ // albo dla liczenia odległości przy pomocy cięciw - te usuwać po przejechaniu
|
||
if (TableAddNew()) {
|
||
// dodanie odcinka do tabelki
|
||
sSpeedTable[iLast].Set(
|
||
pTrack, fCurrentDistance,
|
||
( fLastDir < 0 ?
|
||
spEnabled | spCurve | spReverse :
|
||
spEnabled | spCurve ) );
|
||
}
|
||
}
|
||
}
|
||
|
||
fCurrentDistance += fTrackLength; // doliczenie kolejnego odcinka do przeskanowanej długości
|
||
tLast = pTrack; // odhaczenie, że sprawdzony
|
||
fLastVel = pTrack->VelocityGet(); // prędkość na poprzednio sprawdzonym odcinku
|
||
pTrack = pTrack->Connected(
|
||
( pTrack->eType == tt_Cross ?
|
||
(sSpeedTable[iLast].iFlags >> 28) :
|
||
static_cast<int>(fLastDir) ),
|
||
fLastDir); // może być NULL
|
||
|
||
if (pTrack != nullptr )
|
||
{ // jeśli kolejny istnieje
|
||
if( tLast != nullptr ) {
|
||
|
||
if( ( tLast->VelocityGet() > 0 )
|
||
&& ( ( tLast->VelocityGet() < pTrack->VelocityGet() )
|
||
|| ( pTrack->VelocityGet() < 0 ) ) ) {
|
||
|
||
if( ( iLast != -1 )
|
||
&& ( sSpeedTable[ iLast ].trTrack == tLast ) ) {
|
||
// if the track is already in the table we only need to mark it as relevant
|
||
sSpeedTable[ iLast ].iFlags |= spEnabled;
|
||
}
|
||
else {
|
||
// otherwise add it
|
||
TableAddNew();
|
||
sSpeedTable[ iLast ].Set(
|
||
tLast,
|
||
fCurrentDistance - fTrackLength, // by now the current distance points to beginning of next track
|
||
( fLastDir > 0 ?
|
||
pTrack->iPrevDirection :
|
||
pTrack->iNextDirection ) ?
|
||
spEnabled :
|
||
spEnabled | spReverse );
|
||
}
|
||
}
|
||
}
|
||
// zwiększenie skanowanej odległości tylko jeśli istnieje dalszy tor
|
||
fTrackLength = pTrack->Length();
|
||
}
|
||
else
|
||
{ // definitywny koniec skanowania, chyba że dalej puszczamy samochód po gruncie...
|
||
if( ( iLast == -1 )
|
||
|| ( ( false == TestFlag( sSpeedTable[iLast].iFlags, spEnabled | spEnd ) )
|
||
&& ( sSpeedTable[iLast].trTrack != tLast ) ) ) {
|
||
// only if we haven't already marked end of the track and if the new track doesn't duplicate last one
|
||
if( TableAddNew() ) {
|
||
// zapisanie ostatniego sprawdzonego toru
|
||
sSpeedTable[iLast].Set(
|
||
tLast, fCurrentDistance - fTrackLength, // by now the current distance points to beginning of next track,
|
||
( fLastDir < 0 ?
|
||
spEnabled | spEnd | spReverse :
|
||
spEnabled | spEnd ));
|
||
}
|
||
}
|
||
else if( sSpeedTable[ iLast ].trTrack == tLast ) {
|
||
// otherwise just mark the last added track as the final one
|
||
// TODO: investigate exactly how we can wind up not marking the last existing track as actual end
|
||
sSpeedTable[ iLast ].iFlags |= spEnd;
|
||
}
|
||
// to ostatnia pozycja, bo NULL nic nie da, a może się podpiąć obrotnica, czy jakieś transportery
|
||
return;
|
||
}
|
||
}
|
||
if( TableAddNew() ) {
|
||
// zapisanie ostatniego sprawdzonego toru
|
||
sSpeedTable[ iLast ].Set(
|
||
pTrack, fCurrentDistance,
|
||
( fLastDir < 0 ?
|
||
spNone | spReverse :
|
||
spNone ) );
|
||
}
|
||
};
|
||
|
||
void TController::TableCheck(double fDistance)
|
||
{ // przeliczenie odległości w tabelce, ewentualnie doskanowanie (bez analizy prędkości itp.)
|
||
if( iTableDirection != iDirection ) {
|
||
// jak zmiana kierunku, to skanujemy od końca składu
|
||
TableTraceRoute( fDistance, pVehicles[ end::rear ] );
|
||
TableSort();
|
||
}
|
||
else if (iTableDirection)
|
||
{ // trzeba sprawdzić, czy coś się zmieniło
|
||
auto const distance = MoveDistanceGet();
|
||
for (auto &sp : sSpeedTable) {
|
||
// aktualizacja odległości dla wszystkich pozycji tabeli
|
||
sp.UpdateDistance(distance);
|
||
}
|
||
MoveDistanceReset(); // kasowanie odległości po aktualizacji tabelki
|
||
for( int i = 0; i < iLast; ++i )
|
||
{ // aktualizacja rekordów z wyjątkiem ostatniego
|
||
if (sSpeedTable[i].iFlags & spEnabled) // jeśli pozycja istotna
|
||
{
|
||
if (sSpeedTable[i].Update())
|
||
{
|
||
if( Global.iWriteLogEnabled & 8 ) {
|
||
WriteLog( "Speed table for " + OwnerName() + " detected switch change at " + sSpeedTable[ i ].trTrack->name() + " (generating fresh trace)" );
|
||
}
|
||
// usuwamy wszystko za tym torem
|
||
while (sSpeedTable.back().trTrack != sSpeedTable[i].trTrack)
|
||
{ // usuwamy wszystko dopóki nie trafimy na tą zwrotnicę
|
||
sSpeedTable.pop_back();
|
||
--iLast;
|
||
}
|
||
tLast = sSpeedTable[ i ].trTrack;
|
||
TableTraceRoute( fDistance, pVehicles[ end::rear ] );
|
||
TableSort();
|
||
// nie kontynuujemy pętli, trzeba doskanować ciąg dalszy
|
||
break;
|
||
}
|
||
if (sSpeedTable[i].iFlags & spTrack) // jeśli odcinek
|
||
{
|
||
if( sSpeedTable[ i ].fDist + sSpeedTable[ i ].trTrack->Length() < -fLength ) // a skład wyjechał całą długością poza
|
||
{ // degradacja pozycji
|
||
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)
|
||
}
|
||
}
|
||
}
|
||
else if (sSpeedTable[i].iFlags & spEvent) // jeśli event
|
||
{
|
||
if (sSpeedTable[i].fDist < (
|
||
typeid( *(sSpeedTable[i].evEvent) ) == typeid( putvalues_event ) ?
|
||
-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 &= ~spEnabled; // degradacja pozycji dla samochodu;
|
||
// semafory usuwane tylko przy sprawdzaniu, bo wysyłają komendy
|
||
}
|
||
}
|
||
}
|
||
}
|
||
sSpeedTable[iLast].Update(); // aktualizacja ostatniego
|
||
// WriteLog("TableCheck: Upate last track. Dist=" + AnsiString(sSpeedTable[iLast].fDist));
|
||
if( sSpeedTable[ iLast ].fDist < fDistance ) {
|
||
TableTraceRoute( fDistance, pVehicles[ end::rear ] ); // doskanowanie dalszego odcinka
|
||
TableSort();
|
||
}
|
||
// garbage collection
|
||
TablePurger();
|
||
}
|
||
};
|
||
|
||
auto const passengerstopmaxdistance { 400.0 }; // maximum allowed distance between passenger stop point and consist head
|
||
|
||
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
|
||
double d_to_next_sem = 10000.0; //ustaiwamy na pewno dalej niż widzi AI
|
||
IsAtPassengerStop = false;
|
||
TCommandType go = TCommandType::cm_Unknown;
|
||
eSignNext = NULL;
|
||
// te flagi są ustawiane tutaj, w razie potrzeby
|
||
iDrivigFlags &= ~(moveTrackEnd | moveSwitchFound | moveSemaphorFound | /*moveSpeedLimitFound*/ moveStopPointFound );
|
||
|
||
for( std::size_t i = 0; i < sSpeedTable.size(); ++i )
|
||
{ // 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
|
||
iDrivigFlags |= moveStopPointFound;
|
||
// stop points are irrelevant when not in one of the basic modes
|
||
if( ( OrderCurrentGet() & ( Shunt | Loose_shunt | Obey_train | Bank ) ) == 0 ) { continue; }
|
||
// first 19 chars of the command is expected to be "PassengerStopPoint:" so we skip them
|
||
if ( ToLower(sSpeedTable[i].evEvent->input_text()).compare( 19, sizeof(asNextStop), ToLower(asNextStop)) != 0 )
|
||
{ // jeśli nazwa nie jest zgodna
|
||
if (sSpeedTable[i].fDist < 300.0 && sSpeedTable[i].fDist > 0) // tylko jeśli W4 jest blisko, przy dwóch może zaczać szaleć
|
||
{
|
||
// porównuje do następnej stacji, więc trzeba przewinąć do poprzedniej
|
||
// nastepnie ustawić następną na aktualną tak żeby prawidłowo ją obsłużył w następnym kroku
|
||
if( true == TrainParams->RewindTimeTable( sSpeedTable[ i ].evEvent->input_text() ) ) {
|
||
asNextStop = TrainParams->NextStop();
|
||
iStationStart = TrainParams->StationIndex;
|
||
}
|
||
}
|
||
else 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
|
||
// przy 160km/h jedzie 44m/s, to da dokładność rzędu 5 sekund
|
||
if (sSpeedTable[i].fDist < passengerstopmaxdistance * 0.5 ) {
|
||
// zaliczamy posterunek w pewnej odległości przed (choć W4 nie zasłania już semafora)
|
||
#if LOGSTOPS
|
||
WriteLog(
|
||
pVehicle->asName + " as " + TrainParams->TrainName
|
||
+ ": at " + std::to_string(simulation::Time.data().wHour) + ":" + std::to_string(simulation::Time.data().wMinute)
|
||
+ " passed " + asNextStop); // informacja
|
||
#endif
|
||
// przy jakim dystansie (stanie licznika) ma przesunąć na następny postój
|
||
fLastStopExpDist = mvOccupied->DistCounter + 0.250 + 0.001 * fLength;
|
||
TrainParams->UpdateMTable( simulation::Time, asNextStop );
|
||
UpdateDelayFlag();
|
||
TrainParams->StationIndexInc(); // przejście do następnej
|
||
asNextStop = TrainParams->NextStop(); // pobranie kolejnego miejsca zatrzymania
|
||
sSpeedTable[i].iFlags = 0; // nie liczy się już
|
||
continue; // nie analizować prędkości
|
||
}
|
||
} // koniec obsługi przelotu na W4
|
||
else {
|
||
// zatrzymanie na W4
|
||
if ( false == sSpeedTable[i].bMoved ) {
|
||
// potentially shift the stop point in accordance with its defined parameters
|
||
/*
|
||
// https://rainsted.com/pl/Wersja/18.2.133#Okr.C4.99gi_dla_W4_i_W32
|
||
Pierwszy parametr ujemny - preferowane zatrzymanie czoła składu (np. przed przejściem).
|
||
Pierwszy parametr dodatni - preferowane zatrzymanie środka składu (np. przy wiacie, przejściu podziemnym).
|
||
Drugi parametr ujemny - wskazanie zatrzymania dla krótszych składów (W32).
|
||
Drugi paramer dodatni - długość peronu (W4).
|
||
*/
|
||
auto L = 0.0;
|
||
auto Par1 = sSpeedTable[i].evEvent->input_value(1);
|
||
auto Par2 = sSpeedTable[i].evEvent->input_value(2);
|
||
if ((Par2 >= 0) || (fLength < -Par2)) { //użyj tego W4
|
||
if (Par1 < 0) {
|
||
L = -Par1;
|
||
}
|
||
else {
|
||
//środek
|
||
L = Par1 - fMinProximityDist - fLength * 0.5;
|
||
}
|
||
L = std::max(0.0, std::min(L, std::abs(Par2) - fMinProximityDist - fLength));
|
||
sSpeedTable[i].UpdateDistance(L);
|
||
sSpeedTable[i].bMoved = true;
|
||
}
|
||
else {
|
||
sSpeedTable[i].iFlags = 0;
|
||
}
|
||
}
|
||
IsAtPassengerStop = (
|
||
( sSpeedTable[ i ].fDist <= passengerstopmaxdistance )
|
||
// Ra 2F1I: odległość plus długość pociągu musi być mniejsza od długości
|
||
// peronu, chyba że pociąg jest dłuższy, to wtedy minimalna.
|
||
// jeśli długość peronu ((sSpeedTable[i].evEvent->ValueGet(2)) nie podana,
|
||
// przyjąć odległość fMinProximityDist
|
||
&& ( ( iDrivigFlags & moveStopCloser ) != 0 ?
|
||
sSpeedTable[ i ].fDist + fLength <=
|
||
std::max(
|
||
std::abs( sSpeedTable[ i ].evEvent->input_value( 2 ) ),
|
||
2.0 * fMaxProximityDist + fLength ) : // fmaxproximitydist typically equals ~50 m
|
||
sSpeedTable[ i ].fDist < d_to_next_sem ) );
|
||
|
||
if( !eSignNext ) {
|
||
//jeśli nie widzi następnego sygnału ustawia dotychczasową
|
||
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) to będzie zatrzymanie
|
||
sSpeedTable[ i ].fVelNext = 0;
|
||
} else if( true == IsAtPassengerStop ) {
|
||
// jeśli się zatrzymał przy W4, albo stał w momencie zobaczenia W4
|
||
if( !AIControllFlag ) {
|
||
// w razie przełączenia na AI ma nie podciągać do W4, gdy użytkownik zatrzymał za daleko
|
||
iDrivigFlags &= ~moveStopCloser;
|
||
}
|
||
|
||
if( ( iDrivigFlags & moveDoorOpened ) == 0 ) {
|
||
// drzwi otwierać jednorazowo
|
||
iDrivigFlags |= moveDoorOpened; // nie wykonywać drugi raz
|
||
Doors( true, static_cast<int>( std::floor( std::abs( sSpeedTable[ i ].evEvent->input_value( 2 ) ) ) ) % 10 );
|
||
}
|
||
|
||
if (TrainParams->UpdateMTable( simulation::Time, asNextStop) ) {
|
||
// to się wykona tylko raz po zatrzymaniu na W4
|
||
if( TrainParams->StationIndex < TrainParams->StationCount ) {
|
||
// jeśli są dalsze stacje, bez trąbienia przed odjazdem
|
||
// also ignore any horn cue that may be potentially set below 1 km/h and before the actual full stop
|
||
iDrivigFlags &= ~( moveStartHorn | moveStartHornNow );
|
||
}
|
||
UpdateDelayFlag();
|
||
|
||
// perform loading/unloading
|
||
auto const platformside = static_cast<int>( std::floor( std::abs( sSpeedTable[ i ].evEvent->input_value( 2 ) ) ) ) % 10;
|
||
auto const exchangetime = simulation::Station.update_load( pVehicles[ 0 ], *TrainParams, platformside );
|
||
WaitingSet( exchangetime );
|
||
|
||
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...
|
||
// pozwolenie na przejechanie za W4 przed czasem i nie ma stać
|
||
iDrivigFlags &= ~( moveStopPoint | moveStopHere );
|
||
}
|
||
// przejście do kolejnego rozkazu (zmiana kierunku, odczepianie)
|
||
JumpToNextOrder();
|
||
// ma nie podjeżdżać pod W4 po przeciwnej stronie
|
||
iDrivigFlags &= ~moveStopCloser;
|
||
// ten W4 nie liczy się już zupełnie (nie wyśle SetVelocity)
|
||
sSpeedTable[i].iFlags = 0;
|
||
// jechać
|
||
sSpeedTable[i].fVelNext = -1;
|
||
// nie analizować prędkości
|
||
continue;
|
||
}
|
||
}
|
||
|
||
if (OrderCurrentGet() & ( Shunt | Loose_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(simulation::Time.data().wHour, simulation::Time.data().wMinute + simulation::Time.data().wSecond*0.0167 )) {
|
||
// z dalszą akcją czekamy do godziny odjazdu
|
||
IsAtPassengerStop = false;
|
||
// przy jakim dystansie (stanie licznika) ma przesunąć na następny postój
|
||
fLastStopExpDist = mvOccupied->DistCounter + 0.050 + 0.001 * fLength;
|
||
TrainParams->StationIndexInc(); // przejście do następnej
|
||
asNextStop = TrainParams->NextStop(); // pobranie kolejnego miejsca zatrzymania
|
||
#if LOGSTOPS
|
||
WriteLog(
|
||
pVehicle->asName + " as " + TrainParams->TrainName
|
||
+ ": at " + std::to_string(simulation::Time.data().wHour) + ":" + std::to_string(simulation::Time.data().wMinute)
|
||
+ " next " + asNextStop); // informacja
|
||
#endif
|
||
// update consist weight, brake settings and ai braking tables
|
||
// NOTE: this calculation is expected to run after completing loading/unloading
|
||
CheckVehicles(); // nastawianie hamulca do jazdy pociągowej
|
||
|
||
if( static_cast<int>( std::floor( std::abs( sSpeedTable[ i ].evEvent->input_value( 1 ) ) ) ) % 2 ) {
|
||
// nie podjeżdżać do semafora, jeśli droga nie jest wolna
|
||
iDrivigFlags |= moveStopHere;
|
||
}
|
||
else {
|
||
//po czasie jedź dalej
|
||
iDrivigFlags &= ~moveStopHere;
|
||
}
|
||
iDrivigFlags |= moveStopCloser; // do następnego W4 podjechać blisko (z dociąganiem)
|
||
sSpeedTable[i].iFlags = 0; // nie liczy się już zupełnie (nie wyśle SetVelocity)
|
||
sSpeedTable[i].fVelNext = -1; // można jechać za W4
|
||
if( ( sSpeedTable[ i ].fDist <= 0.0 ) && ( eSignNext == sSpeedTable[ i ].evEvent ) ) {
|
||
// sanity check, if we're held by this stop point, let us go
|
||
VelSignalLast = -1;
|
||
}
|
||
if (go == TCommandType::cm_Unknown) // jeśli nie było komendy wcześniej
|
||
go = TCommandType::cm_Ready; // gotów do odjazdu z W4 (semafor może zatrzymać)
|
||
if( false == tsGuardSignal.empty() ) {
|
||
// 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 " + std::to_string(simulation::Time.data().wHour) + ":" + std::to_string(simulation::Time.data().wMinute)
|
||
+ " end of route."); // informacja
|
||
#endif
|
||
asNextStop = TrainParams->NextStop(); // informacja o końcu trasy
|
||
TrainParams->NewName("none"); // czyszczenie nieaktualnego rozkładu
|
||
// ma nie podjeżdżać pod W4 i ma je pomijać
|
||
iDrivigFlags &= ~( moveStopCloser );
|
||
if( false == TestFlag( iDrivigFlags, movePushPull ) ) {
|
||
// if the consist can change direction through a simple cab change it doesn't need fiddling with recognition of passenger stops
|
||
iDrivigFlags &= ~( moveStopPoint );
|
||
}
|
||
fLastStopExpDist = -1.0f; // nie ma rozkładu, nie ma usuwania stacji
|
||
sSpeedTable[i].iFlags = 0; // W4 nie liczy się już (nie wyśle SetVelocity)
|
||
sSpeedTable[i].fVelNext = -1; // można jechać za W4
|
||
if( ( sSpeedTable[ i ].fDist <= 0.0 ) && ( eSignNext == sSpeedTable[ i ].evEvent ) ) {
|
||
// sanity check, if we're held by this stop point, let us go
|
||
VelSignalLast = -1;
|
||
}
|
||
// wykonanie kolejnego rozkazu (Change_direction albo Shunt)
|
||
JumpToNextOrder();
|
||
// ma się nie ruszać aż do momentu podania sygnału
|
||
iDrivigFlags |= moveStopHere | moveStartHorn;
|
||
continue; // nie analizować prędkości
|
||
} // koniec obsługi ostatniej stacji
|
||
} // vel 0, at passenger stop
|
||
else {
|
||
// HACK: momentarily deactivate W4 to trick the controller into moving closer
|
||
sSpeedTable[ i ].fVelNext = -1;
|
||
} // vel 0, outside of passenger stop
|
||
} // 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ł
|
||
if( sSpeedTable[ i ].fDist < 0.0 ) {
|
||
// sprawdzanie eventów pasywnych miniętych
|
||
/*
|
||
if( ( eSignNext != nullptr ) && ( sSpeedTable[ i ].evEvent == eSignNext ) ) {
|
||
VelSignalLast = sSpeedTable[ i ].fVelNext;
|
||
}
|
||
*/
|
||
if( SemNextIndex == i ) {
|
||
if( Global.iWriteLogEnabled & 8 ) {
|
||
WriteLog( "Speed table update for " + OwnerName() + ", passed semaphor " + sSpeedTable[ SemNextIndex ].GetName() );
|
||
}
|
||
SemNextIndex = -1; // jeśli minęliśmy semafor od ograniczenia to go kasujemy ze zmiennej sprawdzającej dla skanowania w przód
|
||
}
|
||
if( SemNextStopIndex == i ) {
|
||
if( Global.iWriteLogEnabled & 8 ) {
|
||
WriteLog( "Speed table update for " + OwnerName() + ", passed semaphor " + sSpeedTable[ SemNextStopIndex ].GetName() );
|
||
}
|
||
SemNextStopIndex = -1; // jeśli minęliśmy semafor od ograniczenia to go kasujemy ze zmiennej sprawdzającej dla skanowania w przód
|
||
}
|
||
}
|
||
if( sSpeedTable[ i ].fDist > 0.0 ) {
|
||
// check signals ahead
|
||
if( sSpeedTable[ i ].IsProperSemaphor( OrderCurrentGet() ) ) {
|
||
if( SemNextIndex == -1 ) {
|
||
// jeśli jest mienięty poprzedni semafor a wcześniej
|
||
// byl nowy to go dorzucamy do zmiennej, żeby cały czas widział najbliższy
|
||
SemNextIndex = i;
|
||
if( Global.iWriteLogEnabled & 8 ) {
|
||
WriteLog( "Speed table update for " + OwnerName() + ", next semaphor is " + sSpeedTable[ SemNextIndex ].GetName() );
|
||
}
|
||
}
|
||
if( ( SemNextStopIndex == -1 )
|
||
|| ( ( sSpeedTable[ SemNextStopIndex ].fVelNext != 0 )
|
||
&& ( sSpeedTable[ i ].fVelNext == 0 ) ) ) {
|
||
SemNextStopIndex = i;
|
||
}
|
||
}
|
||
}
|
||
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ć?
|
||
if (sSpeedTable[i].fDist < 0.0) // jeśli wskaźnik został minięty
|
||
{
|
||
VelSignalLast = v; //ustawienie prędkości na -1
|
||
}
|
||
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 )
|
||
&& ( Obstacle.distance > 1000 ) ) {
|
||
// jest w maksymalnym zasięgu to można go pominąć (wziąć drugą prędkosć)
|
||
// as long as there isn't any obstacle in arbitrary view range
|
||
eSignSkip = sSpeedTable[ i ].evEvent;
|
||
// 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!
|
||
iDrivigFlags |= moveVisibility;
|
||
// store the ordered restricted speed and don't exceed it until the flag is cleared
|
||
VelRestricted = sSpeedTable[ i ].evEvent->input_value( 2 );
|
||
}
|
||
}
|
||
if( eSignSkip != sSpeedTable[ i ].evEvent ) {
|
||
// jeśli ten SBL nie jest do pominięcia to ma 0 odczytywać
|
||
v = sSpeedTable[ i ].evEvent->input_value( 1 );
|
||
// TODO sprawdzić do której zmiennej jest przypisywane v i zmienić to tutaj
|
||
}
|
||
}
|
||
else if (sSpeedTable[i].IsProperSemaphor(OrderCurrentGet()))
|
||
{ // 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
|
||
d_to_next_sem = std::min(sSpeedTable[i].fDist, d_to_next_sem);
|
||
}
|
||
if( sSpeedTable[ i ].fDist <= d_to_next_sem )
|
||
{
|
||
VelSignalNext = sSpeedTable[ i ].fVelNext;
|
||
}
|
||
}
|
||
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)
|
||
{
|
||
if (Global.iWriteLogEnabled & 8)
|
||
WriteLog("TableUpdate: Event is behind. SVD = 0: " + sSpeedTable[i].evEvent->m_name);
|
||
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 == min_speed(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;
|
||
if (Global.iWriteLogEnabled & 8)
|
||
WriteLog("TableUpdate: Event is behind. SVD < 0: " + sSpeedTable[i].evEvent->m_name);
|
||
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 == min_speed(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;
|
||
if (Global.iWriteLogEnabled & 8)
|
||
WriteLog("TableUpdate: Event is behind. SVD > 0: " + sSpeedTable[i].evEvent->m_name);
|
||
sSpeedTable[i].iFlags = 0; // wyjechaliśmy poza poprzednie, można skasować
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//sprawdzenie eventów pasywnych przed nami
|
||
if( ( mvOccupied->CategoryFlag & 1 )
|
||
&& ( sSpeedTable[ i ].fDist > Obstacle.distance - 20 ) ) {
|
||
// 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( ( v == 0.0 ) && ( true == TestFlag( OrderCurrentGet(), Obey_train ) ) ) {
|
||
// jeśli tryb pociągowy a tarcze ma ShuntVelocity 0 0
|
||
v = -1; // ignorować, chyba że prędkość stanie się niezerowa
|
||
if( true == TestFlag( sSpeedTable[ i ].iFlags, spElapsed ) ) {
|
||
// a jak przejechana to można usunąć, bo podstawowy automat usuwa tylko niezerowe
|
||
sSpeedTable[ i ].iFlags = 0;
|
||
}
|
||
}
|
||
else if( go == TCommandType::cm_Unknown ) {
|
||
// jeśli jeszcze nie ma komendy
|
||
// komenda jest tylko gdy ma jechać, bo stoi na podstawietabelki
|
||
if( v != 0.0 ) {
|
||
// jeśli nie było komendy wcześniej - pierwsza się liczy - ustawianie VelSignal
|
||
go = TCommandType::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 ) {
|
||
// aby stojący ruszył
|
||
VelSignal = v;
|
||
}
|
||
if( sSpeedTable[ i ].fDist < 0.0 ) {
|
||
// jeśli przejechany
|
||
//!!! ustawienie, gdy przejechany jest lepsze niż wcale, ale to jeszcze nie to
|
||
VelSignal = v;
|
||
// to można usunąć (nie mogą być usuwane w skanowaniu)
|
||
sSpeedTable[ i ].iFlags = 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if( !( sSpeedTable[ i ].iFlags & spSectionVel ) ) {
|
||
//jeśli jakiś event pasywny ale nie ograniczenie
|
||
if( go == TCommandType::cm_Unknown ) {
|
||
// jeśli nie było komendy wcześniej - pierwsza się liczy - ustawianie VelSignal
|
||
if( ( v < 0.0 )
|
||
|| ( v >= 1.0 ) ) {
|
||
// bo wartość 0.1 służy do hamowania tylko
|
||
go = TCommandType::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 ) {
|
||
// aby stojący ruszył
|
||
VelSignal = -1.0;
|
||
}
|
||
if( sSpeedTable[ i ].fDist < 0.0 ) {
|
||
// jeśli przejechany
|
||
VelSignal = ( v == 0.0 ? 0.0 : -1.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 )
|
||
|| ( sSpeedTable[ i ].fVelNext != VelRestricted ) ) {
|
||
// ale inny niż ten, na którym minięto S1, chyba że się już zmieniło
|
||
// sygnał zezwalający na jazdę wyłącza jazdę na widoczność (po S1 na SBL)
|
||
iDrivigFlags &= ~moveVisibility;
|
||
// remove restricted speed
|
||
VelRestricted = -1.0;
|
||
}
|
||
}
|
||
// jeśli nie jest ograniczeniem prędkości to można usunąć
|
||
// (nie mogą być usuwane w skanowaniu)
|
||
sSpeedTable[ i ].iFlags = 0;
|
||
}
|
||
}
|
||
else if( sSpeedTable[ i ].evEvent->is_command() ) {
|
||
// jeśli prędkość jest zerowa, a komórka zawiera komendę
|
||
eSignNext = sSpeedTable[ i ].evEvent; // dla informacji
|
||
if( true == TestFlag( iDrivigFlags, moveStopHere ) ) {
|
||
// jeśli ma stać, dostaje komendę od razu
|
||
go = TCommandType::cm_Command; // komenda z komórki, do wykonania po zatrzymaniu
|
||
}
|
||
else if( sSpeedTable[ i ].fDist <= fMaxProximityDist ) {
|
||
// jeśli ma dociągnąć, to niech dociąga
|
||
// (moveStopCloser dotyczy dociągania do W4, nie semafora)
|
||
go = TCommandType::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( ( d > 0.0 )
|
||
&& ( false == TestFlag( sSpeedTable[ i ].iFlags, spElapsed ) ) ) {
|
||
// 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.01 )
|
||
&& ( true == TestFlag( sSpeedTable[ i ].iFlags, ( spEnabled | spEvent | spPassengerStopPoint ) ) )
|
||
&& ( false == IsAtPassengerStop ) ) {
|
||
// ma podjechać bliżej - czy na pewno w tym miejscu taki warunek?
|
||
a = ( ( d > passengerstopmaxdistance ) || ( ( iDrivigFlags & moveStopCloser ) != 0 ) ?
|
||
fAcc :
|
||
0.0 );
|
||
}
|
||
else {
|
||
// przyspieszenie: ujemne, gdy trzeba hamować
|
||
a = ( v * v - mvOccupied->Vel * mvOccupied->Vel ) / ( 25.92 * d );
|
||
if( ( mvOccupied->Vel < v )
|
||
|| ( v == 0.0 ) ) {
|
||
// if we're going slower than the target velocity and there's enough room for safe stop, speed up
|
||
auto const brakingdistance { 1.2 * fBrakeDist * braking_distance_multiplier( v ) };
|
||
if( brakingdistance > 0.0 ) {
|
||
// maintain desired acc while we have enough room to brake safely, when close enough start paying attention
|
||
// try to make a smooth transition instead of sharp change
|
||
a = interpolate( a, AccPreferred, clamp( ( d - brakingdistance ) / brakingdistance, 0.0, 1.0 ) );
|
||
}
|
||
}
|
||
if( ( d < fMinProximityDist )
|
||
&& ( v < fVelDes ) ) {
|
||
// jak jest już blisko, ograniczenie aktualnej prędkości
|
||
fVelDes = v;
|
||
}
|
||
}
|
||
}
|
||
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 + sSpeedTable[ i ].trTrack->Length() < -fLength )
|
||
continue; // zapętlenie, jeśli już wyjechał za ten odcinek
|
||
if( v < fVelDes ) {
|
||
// ograniczenie aktualnej prędkości aż do wyjechania za ograniczenie
|
||
fVelDes = v;
|
||
}
|
||
if( ( sSpeedTable[ i ].iFlags & spEnd )
|
||
&& ( mvOccupied->CategoryFlag & 1 ) ) {
|
||
// if the railway track ends here set the velnext accordingly as well
|
||
// TODO: test this with turntables and such
|
||
fNext = 0.0;
|
||
}
|
||
// 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 ?
|
||
fAcc :
|
||
mvOccupied->Vel < 0.01 ?
|
||
0.0 : // already standing still so no need to bother with brakes
|
||
-2.0 ); // ruszanie albo hamowanie
|
||
|
||
if ((a < fAcc) && (v == std::min(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
|
||
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
|
||
|
||
// jeśli mieliśmy ograniczenie z semafora i nie ma przed nami
|
||
if( ( VelSignalLast >= 0.0 )
|
||
&& ( ( iDrivigFlags & ( moveSemaphorFound | moveSwitchFound | moveStopPointFound ) ) == 0 )
|
||
&& ( true == TestFlag( OrderCurrentGet(), Obey_train ) ) ) {
|
||
VelSignalLast = -1.0;
|
||
}
|
||
|
||
//analiza spisanych z tabelki ograniczeń i nadpisanie aktualnego
|
||
if( ( true == IsAtPassengerStop ) && ( mvOccupied->Vel < 0.01 ) ) {
|
||
// if stopped at a valid passenger stop, hold there
|
||
fVelDes = 0.0;
|
||
}
|
||
else {
|
||
fVelDes = min_speed( fVelDes, VelSignalLast );
|
||
fVelDes = min_speed( fVelDes, VelLimitLast );
|
||
fVelDes = min_speed( fVelDes, VelRoad );
|
||
fVelDes = min_speed( fVelDes, VelRestricted );
|
||
}
|
||
// nastepnego semafora albo zwrotnicy to uznajemy, że mijamy W5
|
||
FirstSemaphorDist = d_to_next_sem; // przepisanie znalezionej wartosci do zmiennej
|
||
return go;
|
||
};
|
||
|
||
// modifies brake distance for low target speeds, to ease braking rate in such situations
|
||
float
|
||
TController::braking_distance_multiplier( float const Targetvelocity ) const {
|
||
|
||
if( Targetvelocity > 65.f ) { return 1.f; }
|
||
if( Targetvelocity < 5.f ) {
|
||
// HACK: engaged automatic transmission means extra/earlier braking effort is needed for the last leg before full stop
|
||
if( ( mvOccupied->TrainType == dt_DMU )
|
||
&& ( mvOccupied->Vel < 40.0 )
|
||
&& ( Targetvelocity == 0.f ) ) {
|
||
auto const multiplier { clamp( 1.f + iVehicles * 0.5f, 2.f, 4.f ) };
|
||
return interpolate( multiplier, 1.f, static_cast<float>( mvOccupied->Vel / 40.0 ) );
|
||
}
|
||
// HACK: cargo trains or trains going downhill with high braking threshold need more distance to come to a full stop
|
||
if( ( fBrake_a0[ 1 ] > 0.2 )
|
||
&& ( ( true == IsCargoTrain )
|
||
|| ( fAccGravity > 0.025 ) ) ) {
|
||
return interpolate(
|
||
1.f, 2.f,
|
||
clamp(
|
||
( fBrake_a0[ 1 ] - 0.2 ) / 0.2,
|
||
0.0, 1.0 ) );
|
||
}
|
||
|
||
return 1.f;
|
||
}
|
||
// stretch the braking distance up to 3 times; the lower the speed, the greater the stretch
|
||
return interpolate( 3.f, 1.f, ( Targetvelocity - 5.f ) / 60.f );
|
||
}
|
||
|
||
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)
|
||
if( sSpeedTable.size() < 2 ) {
|
||
return;
|
||
}
|
||
// simplest approach should be good enough for start -- just copy whatever is still relevant, then swap
|
||
// do a trial run first, to see if we need to bother at all
|
||
std::size_t trimcount{ 0 };
|
||
for( std::size_t idx = 0; idx < sSpeedTable.size() - 1; ++idx ) {
|
||
auto const &speedpoint = sSpeedTable[ idx ];
|
||
if( ( 0 == ( speedpoint.iFlags & spEnabled ) )
|
||
|| ( ( ( speedpoint.iFlags & ( spElapsed | spTrack | spCurve | spSwitch ) ) == ( spElapsed | spTrack | spCurve ) )
|
||
&& ( speedpoint.fVelNext < 0.0 ) ) ) {
|
||
// NOTE: we could break out early here, but running through entire thing gives us exact size needed for new table
|
||
++trimcount;
|
||
}
|
||
}
|
||
if( trimcount == 0 ) {
|
||
// there'd be no gain, may as well bail
|
||
return;
|
||
}
|
||
std::vector<TSpeedPos> trimmedtable; trimmedtable.reserve( sSpeedTable.size() - trimcount );
|
||
// we can only update pointers safely after new table is finalized, so record their indices until then
|
||
for( std::size_t idx = 0; idx < sSpeedTable.size() - 1; ++idx ) {
|
||
// cache placement of semaphors in the new table, if we encounter them
|
||
if( idx == SemNextIndex ) {
|
||
SemNextIndex = trimmedtable.size();
|
||
}
|
||
if( idx == SemNextStopIndex ) {
|
||
SemNextStopIndex = trimmedtable.size();
|
||
}
|
||
auto const &speedpoint = sSpeedTable[ idx ];
|
||
if( ( 0 == ( speedpoint.iFlags & spEnabled ) )
|
||
|| ( ( ( speedpoint.iFlags & ( spElapsed | spTrack | spCurve | spSwitch ) ) == ( spElapsed | spTrack | spCurve ) )
|
||
&& ( speedpoint.fVelNext < 0.0 ) ) ) {
|
||
// if the trimmed point happens to be currently active semaphor we need to invalidate their placements
|
||
if( idx == SemNextIndex ) {
|
||
SemNextIndex = -1;
|
||
}
|
||
if( idx == SemNextStopIndex ) {
|
||
SemNextStopIndex = -1;
|
||
}
|
||
continue;
|
||
}
|
||
// we're left with useful speed point record we should copy
|
||
trimmedtable.emplace_back( speedpoint );
|
||
}
|
||
// always copy the last entry
|
||
trimmedtable.emplace_back( sSpeedTable.back() );
|
||
|
||
if( Global.iWriteLogEnabled & 8 ) {
|
||
WriteLog( "Speed table garbage collection for " + OwnerName() + " cut away " + std::to_string( trimcount ) + ( trimcount == 1 ? " record" : " records" ) );
|
||
}
|
||
// update the data
|
||
std::swap( sSpeedTable, trimmedtable );
|
||
iLast = sSpeedTable.size() - 1;
|
||
};
|
||
|
||
void TController::TableSort() {
|
||
|
||
if( sSpeedTable.size() < 3 ) {
|
||
// we skip last slot and no point in checking if there's only one other entry
|
||
return;
|
||
}
|
||
TSpeedPos sp_temp = TSpeedPos(); // uzywany do przenoszenia
|
||
for( int i = 0; i < ( iLast - 1 ); ++i ) {
|
||
// pętla tylko do dwóch pozycji od końca bo ostatniej nie modyfikujemy
|
||
if (sSpeedTable[i].fDist > sSpeedTable[i + 1].fDist)
|
||
{ // jesli pozycja wcześniejsza jest dalej to źle
|
||
sp_temp = sSpeedTable[i + 1];
|
||
sSpeedTable[i + 1] = sSpeedTable[i]; // zamiana
|
||
sSpeedTable[i] = sp_temp;
|
||
// jeszcze sprawdzenie czy pozycja nie była indeksowana dla eventów
|
||
if (SemNextIndex == i)
|
||
++SemNextIndex;
|
||
else if (SemNextIndex == i + 1)
|
||
--SemNextIndex;
|
||
if (SemNextStopIndex == i)
|
||
++SemNextStopIndex;
|
||
else if (SemNextStopIndex == i + 1)
|
||
--SemNextStopIndex;
|
||
}
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
|
||
TController::TController(bool AI, TDynamicObject *NewControll, bool InitPsyche, bool primary) :// czy ma aktywnie prowadzić?
|
||
AIControllFlag( AI ), pVehicle( NewControll )
|
||
{
|
||
ControllingSet(); // utworzenie połączenia do sterowanego pojazdu
|
||
if( pVehicle != nullptr ) {
|
||
pVehicles[ 0 ] = pVehicle->GetFirstDynamic( 0 ); // pierwszy w kierunku jazdy (Np. Pc1)
|
||
pVehicles[ 1 ] = pVehicle->GetFirstDynamic( 1 ); // ostatni w kierunku jazdy (końcówki)
|
||
}
|
||
else {
|
||
pVehicles[ 0 ] = nullptr;
|
||
pVehicles[ 1 ] = nullptr;
|
||
}
|
||
if( mvOccupied != nullptr ) {
|
||
iDirectionOrder = mvOccupied->CabNo; // 1=do przodu (w kierunku sprzęgu 0)
|
||
VehicleName = mvOccupied->Name;
|
||
|
||
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
|
||
}
|
||
|
||
// fAccThreshold może podlegać uczeniu się - hamowanie powinno być rejestrowane, a potem analizowane
|
||
// próg opóźnienia dla zadziałania hamulca
|
||
fAccThreshold = (
|
||
mvOccupied->TrainType == dt_EZT ? -0.55 :
|
||
mvOccupied->TrainType == dt_DMU ? -0.45 :
|
||
-0.2 );
|
||
// HACK: emu with induction motors need to start their braking a bit sooner than the ones with series motors
|
||
if( ( mvOccupied->TrainType == dt_EZT )
|
||
&& ( mvControlling->EngineType == TEngineType::ElectricInductionMotor ) ) {
|
||
fAccThreshold += 0.10;
|
||
}
|
||
}
|
||
// TrainParams=NewTrainParams;
|
||
// if (TrainParams)
|
||
// asNextStop=TrainParams->NextStop();
|
||
// else
|
||
TrainParams = new TTrainParameters("none"); // rozkład jazdy
|
||
// OrderCommand="";
|
||
// OrderValue=0;
|
||
OrdersClear();
|
||
|
||
if( true == primary ) {
|
||
iDrivigFlags |= movePrimary; // aktywnie prowadzące pojazd
|
||
}
|
||
|
||
SetDriverPsyche(); // na końcu, bo wymaga ustawienia zmiennych
|
||
TableClear();
|
||
|
||
if( WriteLogFlag ) {
|
||
#ifdef _WIN32
|
||
_mkdir( "physicslog" );
|
||
#elif __linux__
|
||
mkdir( "physicslog", 0644 );
|
||
#endif
|
||
LogFile.open( std::string( "physicslog/" + VehicleName + ".dat" ),
|
||
std::ios::in | std::ios::out | std::ios::trunc );
|
||
#if LOGPRESS == 0
|
||
LogFile
|
||
<< "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 Direction Command CVal1 CVal2 "
|
||
<< "Security Wheelslip "
|
||
<< "EngineTemp[Deg] OilTemp[Deg] WaterTemp[Deg] WaterAuxTemp[Deg]"
|
||
<< "\r\n";
|
||
LogFile << std::fixed << std::setprecision( 4 );
|
||
#endif
|
||
#if LOGPRESS == 1
|
||
LogFile << string( "t\tVel\tAcc\tPP\tVVP\tBP\tBVP\tCVP" ).c_str() << "\n";
|
||
#endif
|
||
LogFile.flush();
|
||
}
|
||
};
|
||
|
||
void TController::CloseLog()
|
||
{
|
||
if (WriteLogFlag)
|
||
{
|
||
LogFile.close();
|
||
}
|
||
};
|
||
|
||
TController::~TController()
|
||
{ // wykopanie mechanika z roboty
|
||
delete TrainParams;
|
||
CloseLog();
|
||
};
|
||
|
||
// zamiana kodu rozkazu na opis
|
||
std::string TController::Order2Str(TOrders Order) const {
|
||
|
||
if( Order & Change_direction ) {
|
||
// może być nałożona na inną i wtedy ma priorytet
|
||
return "Change_direction";
|
||
}
|
||
switch( Order ) {
|
||
case Wait_for_orders: return "Wait_for_orders";
|
||
case Prepare_engine: return "Prepare_engine";
|
||
case Release_engine: return "Release_engine";
|
||
case Change_direction: return "Change_direction";
|
||
case Connect: return "Connect";
|
||
case Disconnect: return "Disconnect";
|
||
case Shunt: return "Shunt";
|
||
case Loose_shunt: return "Loose_shunt";
|
||
case Obey_train: return "Obey_train";
|
||
case Bank: return "Bank";
|
||
case Jump_to_first_order: return "Jump_to_first_order";
|
||
default: return "Undefined";
|
||
}
|
||
}
|
||
|
||
std::array<char, 64> orderbuffer;
|
||
|
||
std::string TController::OrderCurrent() const
|
||
{ // pobranie aktualnego rozkazu celem wyświetlenia
|
||
auto const order { OrderCurrentGet() };
|
||
if( order & Change_direction ) {
|
||
return locale::strings[ locale::string::driver_scenario_changedirection ];
|
||
}
|
||
|
||
switch( OrderList[ OrderPos ] ) {
|
||
case Wait_for_orders: { return locale::strings[ locale::string::driver_scenario_waitfororders ]; }
|
||
case Prepare_engine: { return locale::strings[ locale::string::driver_scenario_prepareengine ]; }
|
||
case Release_engine: { return locale::strings[ locale::string::driver_scenario_releaseengine ]; }
|
||
case Change_direction: { return locale::strings[ locale::string::driver_scenario_changedirection ]; }
|
||
case Connect: { return locale::strings[ locale::string::driver_scenario_connect ]; }
|
||
case Disconnect: {
|
||
if( iVehicleCount < 0 ) {
|
||
// done with uncoupling, order should update shortly
|
||
return locale::strings[ locale::string::driver_scenario_waitfororders ];
|
||
}
|
||
// try to provide some task details
|
||
auto const count { iVehicleCount };
|
||
|
||
if( iVehicleCount > 1 ) {
|
||
std::snprintf(
|
||
orderbuffer.data(), orderbuffer.size(),
|
||
( iVehicleCount < 5 ?
|
||
locale::strings[ locale::string::driver_scenario_fewvehicles ].c_str() : // 2-4
|
||
locale::strings[ locale::string::driver_scenario_somevehicles ].c_str() ), // 5+
|
||
count );
|
||
}
|
||
auto const countstring { (
|
||
count == 0 ? locale::strings[ locale::string::driver_scenario_allvehicles ] :
|
||
count == 1 ? locale::strings[ locale::string::driver_scenario_onevehicle ] :
|
||
orderbuffer.data() ) };
|
||
|
||
std::snprintf(
|
||
orderbuffer.data(), orderbuffer.size(),
|
||
locale::strings[ locale::string::driver_scenario_disconnect ].c_str(),
|
||
countstring.c_str() );
|
||
return orderbuffer.data();
|
||
}
|
||
case Shunt: { return locale::strings[ locale::string::driver_scenario_shunt ]; }
|
||
case Loose_shunt: { return locale::strings[ locale::string::driver_scenario_looseshunt ]; }
|
||
case Obey_train: { return locale::strings[ locale::string::driver_scenario_obeytrain ]; }
|
||
case Bank: { return locale::strings[ locale::string::driver_scenario_bank ]; }
|
||
default: { return{}; }
|
||
}
|
||
};
|
||
|
||
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
|
||
auto const localbrakelevel { mvOccupied->LocalBrakePosA };
|
||
ZeroSpeed();
|
||
ZeroDirection();
|
||
if (TestFlag(d->MoverParameters->Couplers[iDirectionOrder < 0 ? 1 : 0].CouplingFlag, ctrain_controll)) {
|
||
mvControlling->MainSwitch( false); // dezaktywacja czuwaka, jeśli przejście do innego członu
|
||
mvOccupied->DecLocalBrakeLevel(LocalBrakePosNo); // zwolnienie hamulca w opuszczanym pojeździe
|
||
// mvOccupied->BrakeLevelSet((mvOccupied->BrakeHandle==FVel6)?4:-2); //odcięcie na
|
||
// zaworze maszynisty, FVel6 po drugiej stronie nie luzuje
|
||
mvOccupied->BrakeLevelSet(
|
||
mvOccupied->Handle->GetPos(bh_NP)); // odcięcie na zaworze maszynisty
|
||
BrakeLevelSet(gbh_NP); //ustawienie zmiennej GBH
|
||
}
|
||
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
|
||
if( ( simulation::Train )
|
||
&& ( simulation::Train->Dynamic() == old ) ) {
|
||
// ewentualna zmiana kabiny użytkownikowi
|
||
Global.changeDynObj = pVehicle; // uruchomienie protezy
|
||
}
|
||
ControllingSet(); // utworzenie połączenia do sterowanego pojazdu (może się zmienić) - silnikowy dla EZT
|
||
}
|
||
if( mvControlling->EngineType == TEngineType::DieselEngine ) {
|
||
// dla 2Ls150 - przed ustawieniem kierunku - można zmienić tryb pracy
|
||
if( mvControlling->ShuntModeAllow ) {
|
||
mvControlling->CurrentSwitch(
|
||
( ( OrderCurrentGet() & ( Shunt | Loose_shunt ) ) != 0 )
|
||
|| ( 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 (localbrakelevel > 0.0) // hamowanie tylko jeśli był wcześniej zahamowany (bo możliwe, że jedzie!)
|
||
mvOccupied->LocalBrakePosA = localbrakelevel; // 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 < std::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
|
||
if( ( true == AIControllFlag )
|
||
|| ( d != pVehicle ) ) {
|
||
// don't touch human-controlled vehicle, but others are free game
|
||
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)
|
||
}
|
||
//ustawianie trybu pracy zadajnika hamulca, wystarczy raz po inicjalizacji AI
|
||
if( true == AIControllFlag ) {
|
||
// if a human is in charge leave the brake mode up to them, otherwise do as you like
|
||
for( int i = 1; i <= 8; i *= 2 ) {
|
||
if( ( mvOccupied->BrakeOpModes & i ) > 0 ) {
|
||
mvOccupied->BrakeOpModeFlag = i;
|
||
}
|
||
}
|
||
}
|
||
|
||
// teraz zerujemy tabelkę opóźnienia hamowania
|
||
for (int i = 0; i < BrakeAccTableSize; ++i)
|
||
{
|
||
fBrake_a0[i+1] = 0;
|
||
fBrake_a1[i+1] = 0;
|
||
}
|
||
|
||
if( OrderCurrentGet() & ( Shunt | Loose_shunt ) ) {
|
||
// for uniform behaviour and compatibility with older scenarios set default acceleration table values for shunting
|
||
fAccThreshold = (
|
||
mvOccupied->TrainType == dt_EZT ? -0.55 :
|
||
mvOccupied->TrainType == dt_DMU ? -0.45 :
|
||
-0.2 );
|
||
// HACK: emu with induction motors need to start their braking a bit sooner than the ones with series motors
|
||
if( ( mvOccupied->TrainType == dt_EZT )
|
||
&& ( mvControlling->EngineType == TEngineType::ElectricInductionMotor ) ) {
|
||
fAccThreshold += 0.10;
|
||
}
|
||
fNominalAccThreshold = fAccThreshold;
|
||
}
|
||
|
||
if( OrderCurrentGet() & ( Obey_train | Bank ) ) {
|
||
// 4. Przeliczanie siły hamowania
|
||
double const velstep = ( mvOccupied->Vmax*0.5 ) / BrakeAccTableSize;
|
||
d = pVehicles[0]; // pojazd na czele składu
|
||
while (d) {
|
||
for( int i = 0; i < BrakeAccTableSize; ++i ) {
|
||
fBrake_a0[ i + 1 ] += d->MoverParameters->BrakeForceR( 0.25, velstep*( 1 + 2 * i ) );
|
||
fBrake_a1[ i + 1 ] += d->MoverParameters->BrakeForceR( 1.00, velstep*( 1 + 2 * i ) );
|
||
}
|
||
d = d->Next(); // kolejny pojazd, podłączony od tyłu (licząc od czoła)
|
||
}
|
||
for (int i = 0; i < BrakeAccTableSize; ++i)
|
||
{
|
||
fBrake_a1[i+1] -= fBrake_a0[i+1];
|
||
fBrake_a0[i+1] /= fMass;
|
||
fBrake_a0[i + 1] += 0.001*velstep*(1 + 2 * i);
|
||
fBrake_a1[i+1] /= (12*fMass);
|
||
}
|
||
|
||
IsCargoTrain = ( mvOccupied->CategoryFlag == 1 ) && ( ( mvOccupied->BrakeDelayFlag & bdelay_G ) != 0 );
|
||
IsHeavyCargoTrain = ( true == IsCargoTrain ) && ( fBrake_a0[ 1 ] > 0.4 );
|
||
|
||
BrakingInitialLevel = (
|
||
IsHeavyCargoTrain ? 1.25 :
|
||
IsCargoTrain ? 1.25 :
|
||
1.00 );
|
||
|
||
BrakingLevelIncrease = (
|
||
IsHeavyCargoTrain ? 0.25 :
|
||
IsCargoTrain ? 0.25 :
|
||
0.25 );
|
||
|
||
if( mvOccupied->TrainType == dt_EZT ) {
|
||
if( mvControlling->EngineType == TEngineType::ElectricInductionMotor ) {
|
||
// HACK: emu with induction motors need to start their braking a bit sooner than the ones with series motors
|
||
fNominalAccThreshold = std::max( -0.60, -fBrake_a0[ BrakeAccTableSize ] - 8 * fBrake_a1[ BrakeAccTableSize ] );
|
||
}
|
||
else {
|
||
fNominalAccThreshold = std::max( -0.75, -fBrake_a0[ BrakeAccTableSize ] - 8 * fBrake_a1[ BrakeAccTableSize ] );
|
||
}
|
||
fBrakeReaction = 0.25;
|
||
}
|
||
else if( mvOccupied->TrainType == dt_DMU ) {
|
||
fNominalAccThreshold = std::max( -0.75, -fBrake_a0[ BrakeAccTableSize ] - 8 * fBrake_a1[ BrakeAccTableSize ] );
|
||
fBrakeReaction = 0.25;
|
||
}
|
||
else if (ustaw > 16) {
|
||
fNominalAccThreshold = -fBrake_a0[ BrakeAccTableSize ] - 4 * fBrake_a1[ BrakeAccTableSize ];
|
||
fBrakeReaction = 1.00 + fLength*0.004;
|
||
}
|
||
else {
|
||
fNominalAccThreshold = -fBrake_a0[ BrakeAccTableSize ] - 1 * fBrake_a1[ BrakeAccTableSize ];
|
||
fBrakeReaction = 1.00 + fLength*0.005;
|
||
}
|
||
fAccThreshold = fNominalAccThreshold;
|
||
}
|
||
}
|
||
|
||
double TController::ESMVelocity(bool Main)
|
||
{
|
||
double fCurrentCoeff = 0.9;
|
||
double fFrictionCoeff = 0.85;
|
||
double ESMVel = 9999;
|
||
int MCPN = mvControlling->MainCtrlActualPos;
|
||
int SCPN = mvControlling->ScndCtrlActualPos;
|
||
if (Main)
|
||
MCPN += 1;
|
||
else
|
||
SCPN += 1;
|
||
if ((mvControlling->RList[MCPN].ScndAct < 255)&&(mvControlling->ScndCtrlActualPos==0))
|
||
SCPN = mvControlling->RList[MCPN].ScndAct;
|
||
double FrictionMax = mvControlling->Mass*9.81*mvControlling->Adhesive(mvControlling->RunningTrack.friction)*fFrictionCoeff;
|
||
double IF = mvControlling->Imax;
|
||
double MS = 0;
|
||
double Fmax = 0;
|
||
for (int i = 0; i < 5; i++)
|
||
{
|
||
MS = mvControlling->MomentumF(IF, IF, SCPN);
|
||
Fmax = MS * mvControlling->RList[MCPN].Bn * mvControlling->RList[MCPN].Mn * 2 / mvControlling->WheelDiameter * mvControlling->Transmision.Ratio;
|
||
if( Fmax != 0.0 ) {
|
||
IF = 0.5 * IF * ( 1 + FrictionMax / Fmax );
|
||
}
|
||
else {
|
||
// NOTE: gets trimmed to actual highest acceptable value after the loop
|
||
IF = std::numeric_limits<double>::max();
|
||
break;
|
||
}
|
||
}
|
||
IF = std::min(IF, mvControlling->Imax*fCurrentCoeff);
|
||
double R = mvControlling->RList[MCPN].R + mvControlling->CircuitRes + mvControlling->RList[MCPN].Mn*mvControlling->WindingRes;
|
||
double pole = mvControlling->MotorParam[SCPN].fi *
|
||
std::max(abs(IF) / (abs(IF) + mvControlling->MotorParam[SCPN].Isat) - mvControlling->MotorParam[SCPN].fi0, 0.0);
|
||
double Us = abs(mvControlling->Voltage) - IF*R;
|
||
double ns = std::max(0.0, Us / (pole*mvControlling->RList[MCPN].Mn));
|
||
ESMVel = ns * mvControlling->WheelDiameter*M_PI*3.6/mvControlling->Transmision.Ratio;
|
||
return ESMVel;
|
||
}
|
||
|
||
int TController::CheckDirection() {
|
||
|
||
int d = mvOccupied->DirAbsolute; // który sprzęg jest z przodu
|
||
if( !d ) {
|
||
// jeśli nie ma ustalonego kierunku to jedziemy wg aktualnej kabiny
|
||
d = mvOccupied->CabNo;
|
||
}
|
||
return d;
|
||
}
|
||
|
||
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 = CheckDirection();
|
||
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
|
||
fVelMax = min_speed( fVelMax, p->MoverParameters->Vmax ); // ustalenie maksymalnej prędkości dla składu
|
||
// reset oerlikon brakes consist flag as different type was detected
|
||
if( ( p->MoverParameters->BrakeSubsystem != TBrakeSubSystem::ss_ESt )
|
||
&& ( p->MoverParameters->BrakeSubsystem != TBrakeSubSystem::ss_LSt ) ) {
|
||
iDrivigFlags &= ~( moveOerlikons );
|
||
}
|
||
p = p->Neighbour(dir); // pojazd podłączony od wskazanej strony
|
||
}
|
||
if (main)
|
||
iDrivigFlags |= movePrimary; // nie znaleziono innego, można się porządzić
|
||
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)
|
||
{
|
||
// HACK: wagony muszą mieć baterię załączoną do otwarcia drzwi...
|
||
if( ( p != pVehicle )
|
||
&& ( ( p->MoverParameters->Couplers[ end::front ].CouplingFlag & ( coupling::control ) ) == 0 )
|
||
&& ( ( p->MoverParameters->Couplers[ end::rear ].CouplingFlag & ( coupling::control ) ) == 0 ) ) {
|
||
// NOTE: don't set battery in the occupied vehicle, let the user/ai do it explicitly
|
||
p->MoverParameters->BatterySwitch( true );
|
||
// enable heating and converter in carriages with can be heated
|
||
if( p->MoverParameters->HeatingPower > 0 ) {
|
||
p->MoverParameters->HeatingAllow = true;
|
||
p->MoverParameters->ConverterSwitch( true, range_t::local );
|
||
}
|
||
}
|
||
|
||
if (p->asDestination == "none")
|
||
p->DestinationSet(TrainParams->Relation2, TrainParams->TrainName); // 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 == TPowerSource::CurrentCollector)
|
||
{ // jeśli pojazd posiada pantograf, to przydzielamy mu maskę, którą będzie informował o jeździe bezprądowej
|
||
p->iOverheadMask = pantmask;
|
||
pantmask = pantmask << 1; // przesunięcie bitów, max. 32 pojazdy z pantografami w składzie
|
||
}
|
||
d = p->DirectionSet(d ? 1 : -1); // zwraca położenie następnego (1=zgodny,0=odwrócony - względem czoła składu)
|
||
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( true == TestFlag( OrderCurrentGet(), Obey_train ) ) {
|
||
// jeśli jazda pociągowa
|
||
// światła pociągowe (Pc1) i końcówki (Pc5)
|
||
auto const frontlights { (
|
||
( m_lighthints[ end::front ] != -1 ) ?
|
||
m_lighthints[ end::front ] :
|
||
light::headlight_left | light::headlight_right | light::headlight_upper ) };
|
||
auto const rearlights { (
|
||
( m_lighthints[ end::rear ] != -1 ) ?
|
||
m_lighthints[ end::rear ] :
|
||
light::redmarker_left | light::redmarker_right | light::rearendsignals ) };
|
||
Lights(
|
||
frontlights,
|
||
rearlights );
|
||
}
|
||
else if (OrderCurrentGet() & (Shunt | Loose_shunt | Connect))
|
||
{
|
||
// HACK: the 'front' and 'rear' of the consist is determined by current consist direction
|
||
// since direction shouldn't affect Tb1 light configuration, we 'counter' this behaviour by virtually swapping end vehicles
|
||
if( mvOccupied->ActiveDir > 0 ) {
|
||
Lights(
|
||
light::headlight_right,
|
||
( pVehicles[ 1 ]->MoverParameters->CabNo != 0 ?
|
||
light::headlight_left :
|
||
0 ) ); //światła manewrowe (Tb1) na pojeździe z napędem
|
||
}
|
||
else {
|
||
Lights(
|
||
( pVehicles[ 1 ]->MoverParameters->CabNo != 0 ?
|
||
light::headlight_left :
|
||
0 ),
|
||
light::headlight_right ); //światła manewrowe (Tb1) na pojeździe z napędem
|
||
}
|
||
}
|
||
else if( true == TestFlag( OrderCurrentGet(), Disconnect ) ) {
|
||
if( mvOccupied->ActiveDir > 0 ) {
|
||
// jak ma kierunek do przodu
|
||
// światła manewrowe (Tb1) tylko z przodu, aby nie pozostawić odczepionego ze światłem
|
||
Lights( light::headlight_right, 0 );
|
||
}
|
||
else {
|
||
// jak dociska
|
||
// światła manewrowe (Tb1) tylko z przodu, aby nie pozostawić odczepionego ze światłem
|
||
Lights( 0, light::headlight_right );
|
||
}
|
||
}
|
||
|
||
if( OrderCurrentGet() & ( Shunt | Loose_shunt | Obey_train | Bank ) ) {
|
||
// nastawianie hamulca do jazdy pociągowej
|
||
AutoRewident();
|
||
// enable door locks
|
||
mvOccupied->LockDoors( true );
|
||
}
|
||
}
|
||
else { // gdy człowiek i gdy nastąpiło połącznie albo rozłączenie
|
||
// Ra 2014-02: lepiej tu niż w pętli obsługującej komendy, bo tam się zmieni informacja o składzie
|
||
switch (user) {
|
||
case Change_direction: {
|
||
while (OrderCurrentGet() & (Change_direction)) {
|
||
// zmianę kierunku też można olać, ale zmienić kierunek skanowania!
|
||
JumpToNextOrder();
|
||
}
|
||
break;
|
||
}
|
||
case Connect: {
|
||
while (OrderCurrentGet() & (Change_direction)) {
|
||
// zmianę kierunku też można olać, ale zmienić kierunek skanowania!
|
||
JumpToNextOrder();
|
||
}
|
||
if (OrderCurrentGet() & (Connect)) {
|
||
// jeśli miało być łączenie, zakładamy, że jest dobrze (sprawdzić?)
|
||
iCoupler = 0; // koniec z doczepianiem
|
||
iDrivigFlags &= ~moveConnect; // zdjęcie flagi doczepiania
|
||
JumpToNextOrder(); // wykonanie następnej komendy
|
||
if (OrderCurrentGet() & (Change_direction)) {
|
||
// zmianę kierunku też można olać, ale zmienić kierunek skanowania!
|
||
JumpToNextOrder();
|
||
}
|
||
|
||
}
|
||
break;
|
||
}
|
||
case Disconnect: {
|
||
while (OrderCurrentGet() & (Change_direction)) {
|
||
// zmianę kierunku też można olać, ale zmienić kierunek skanowania!
|
||
JumpToNextOrder();
|
||
}
|
||
if (OrderCurrentGet() & (Disconnect)) {
|
||
// wypadało by sprawdzić, czy odczepiono wagony w odpowiednim miejscu (iVehicleCount)
|
||
JumpToNextOrder(); // wykonanie następnej komendy
|
||
if (OrderCurrentGet() & (Change_direction)) {
|
||
// zmianę kierunku też można olać, ale zmienić kierunek skanowania!
|
||
JumpToNextOrder();
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
default: {
|
||
break;
|
||
}
|
||
} // switch
|
||
}
|
||
// Ra 2014-09: tymczasowo prymitywne ustawienie warunku pod kątem SN61
|
||
if( ( mvOccupied->TrainType == dt_EZT )
|
||
|| ( mvOccupied->TrainType == dt_DMU )
|
||
|| ( iVehicles == 1 ) ) {
|
||
// zmiana czoła przez zmianę kabiny
|
||
iDrivigFlags |= movePushPull;
|
||
}
|
||
else {
|
||
// zmiana czoła przez manewry
|
||
iDrivigFlags &= ~movePushPull;
|
||
}
|
||
} // 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
|
||
};
|
||
|
||
void TController::DirectionChange() {
|
||
|
||
auto const initialstate { iDirection };
|
||
iDirection = CheckDirection();
|
||
if( iDirection != initialstate ) {
|
||
CheckVehicles( Change_direction );
|
||
}
|
||
}
|
||
|
||
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
|
||
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( (OrderCurrentGet() & ( Shunt | Loose_shunt | Obey_train | Bank | Connect | Prepare_engine ) ) != 0 ) // 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
|
||
// zatrąb po odhamowaniu
|
||
iDrivigFlags |= moveStartHornNow;
|
||
}
|
||
}
|
||
VelSignal = NewVel; // prędkość zezwolona na aktualnym odcinku
|
||
VelNext = NewVelNext; // prędkość przy następnym obiekcie
|
||
}
|
||
|
||
double TController::BrakeAccFactor() const
|
||
{
|
||
double Factor = 1.0;
|
||
|
||
if( ( fAccThreshold != 0.0 )
|
||
&& ( AccDesired < 0.0 )
|
||
&& ( ( ActualProximityDist > fMinProximityDist )
|
||
|| ( mvOccupied->Vel > VelDesired + fVelPlus ) ) ) {
|
||
Factor += ( fBrakeReaction * ( /*mvOccupied->BrakeCtrlPosR*/BrakeCtrlPosition < 0.5 ? 1.5 : 1 ) ) * mvOccupied->Vel / ( std::max( 0.0, ActualProximityDist ) + 1 ) * ( ( AccDesired - AbsAccS_pub ) / fAccThreshold );
|
||
}
|
||
/*
|
||
if (mvOccupied->TrainType == dt_DMU && mvOccupied->Vel > 40 && VelNext<40)
|
||
Factor *= 1 + 0.25 * ( (1600 - VelNext * VelNext) / (mvOccupied->Vel * mvOccupied->Vel) );
|
||
*/
|
||
return Factor;
|
||
}
|
||
|
||
void TController::SetDriverPsyche()
|
||
{
|
||
if ((Psyche == Aggressive) && (OrderCurrentGet() == Obey_train))
|
||
{
|
||
ReactionTime = HardReactionTime; // w zaleznosci od charakteru maszynisty
|
||
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->MainCtrlPowerPos() < 3)
|
||
ReactionTime = mvControlling->InitialCtrlDelay + ReactionTime;
|
||
if (/* GBH mvOccupied->BrakeCtrlPos*/BrakeCtrlPosition > 1)
|
||
ReactionTime = 0.5 * ReactionTime;
|
||
}
|
||
};
|
||
|
||
bool TController::PrepareEngine()
|
||
{ // odpalanie silnika
|
||
bool OK = false,
|
||
voltfront = false,
|
||
voltrear = false;
|
||
LastReactionTime = 0.0;
|
||
ReactionTime = PrepareTime;
|
||
|
||
if ( mvControlling->EnginePowerSource.SourceType == TPowerSource::CurrentCollector ) {
|
||
voltfront = true;
|
||
voltrear = true;
|
||
}
|
||
else {
|
||
if( mvOccupied->TrainType != dt_EZT ) {
|
||
// Ra 2014-06: to jest wirtualny prąd dla spalinowych???
|
||
voltfront = true;
|
||
}
|
||
}
|
||
auto workingtemperature { true };
|
||
if (AIControllFlag) {
|
||
// część wykonawcza dla sterowania przez komputer
|
||
mvOccupied->BatterySwitch( true );
|
||
if( ( mvControlling->EngineType == TEngineType::DieselElectric )
|
||
|| ( mvControlling->EngineType == TEngineType::DieselEngine ) ) {
|
||
mvControlling->OilPumpSwitch( true );
|
||
workingtemperature = UpdateHeating();
|
||
if( true == workingtemperature ) {
|
||
mvControlling->FuelPumpSwitch( true );
|
||
}
|
||
}
|
||
if (mvControlling->EnginePowerSource.SourceType == TPowerSource::CurrentCollector)
|
||
{ // jeśli silnikowy jest pantografującym
|
||
mvOccupied->PantFront( true );
|
||
mvOccupied->PantRear( true );
|
||
if (mvControlling->PantPress < 4.2) {
|
||
// załączenie małej sprężarki
|
||
if( mvControlling->TrainType != dt_EZT ) {
|
||
// odłączenie zbiornika głównego, bo z nim nie da rady napompować
|
||
mvControlling->bPantKurek3 = false;
|
||
}
|
||
mvControlling->PantCompFlag = true; // załączenie sprężarki pantografów
|
||
}
|
||
else {
|
||
// 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ć
|
||
}
|
||
}
|
||
}
|
||
|
||
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->Vel < 0.01 ) { // 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[ end::rear ].Connected == nullptr ) {
|
||
// jeśli z tyłu nie ma nic
|
||
iDirection = -1; // jazda w kierunku sprzęgu 1
|
||
}
|
||
if( mvOccupied->Couplers[ end::front ].Connected == nullptr ) {
|
||
// 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
|
||
ZeroSpeed();
|
||
mvControlling->ConvOvldFlag = false; // reset nadmiarowego
|
||
}
|
||
else if (false == IsLineBreakerClosed) {
|
||
ZeroSpeed();
|
||
if( mvOccupied->TrainType == dt_SN61 ) {
|
||
// specjalnie dla SN61 żeby nie zgasł
|
||
while( ( mvControlling->RList[ mvControlling->MainCtrlPos ].Mn == 0 )
|
||
&& ( mvControlling->IncMainCtrl( 1 ) ) ) {
|
||
;
|
||
}
|
||
}
|
||
if( ( mvControlling->EnginePowerSource.SourceType != TPowerSource::CurrentCollector )
|
||
|| ( std::max( mvControlling->GetTrainsetVoltage(), std::abs( mvControlling->RunningTraction.TractionVoltage ) ) > mvControlling->EnginePowerSource.CollectorParameters.MinV ) ) {
|
||
mvControlling->MainSwitch( true );
|
||
}
|
||
}
|
||
else {
|
||
OK = ( OrderDirectionChange( iDirection, mvOccupied ) == -1 );
|
||
mvOccupied->ConverterSwitch( true );
|
||
// w EN57 sprężarka w ra jest zasilana z silnikowego
|
||
mvOccupied->CompressorSwitch( true );
|
||
// enable motor blowers
|
||
mvOccupied->MotorBlowersSwitchOff( false, end::front );
|
||
mvOccupied->MotorBlowersSwitch( true, end::front );
|
||
mvOccupied->MotorBlowersSwitchOff( false, end::rear );
|
||
mvOccupied->MotorBlowersSwitch( true, end::rear );
|
||
// enable train brake if it's off
|
||
if( mvOccupied->fBrakeCtrlPos == mvOccupied->Handle->GetPos( bh_NP ) ) {
|
||
mvOccupied->BrakeLevelSet( mvOccupied->Handle->GetPos( bh_RP ) );
|
||
}
|
||
// sync virtual brake state with the 'real' one
|
||
std::unordered_map<int, int> const brakepositions {
|
||
{ static_cast<int>( mvOccupied->Handle->GetPos( bh_RP ) ), gbh_RP },
|
||
{ static_cast<int>( mvOccupied->Handle->GetPos( bh_NP ) ), gbh_NP },
|
||
{ static_cast<int>( mvOccupied->Handle->GetPos( bh_FS ) ), gbh_FS } };
|
||
auto const lookup { brakepositions.find( static_cast<int>( mvOccupied->fBrakeCtrlPos ) ) };
|
||
if( lookup != brakepositions.end() ) {
|
||
BrakeLevelSet( lookup->second ); // GBH
|
||
}
|
||
}
|
||
}
|
||
else
|
||
OK = mvControlling->Mains;
|
||
}
|
||
else
|
||
OK = false;
|
||
|
||
if( ( true == OK )
|
||
&& ( mvOccupied->ActiveDir != 0 )
|
||
&& ( true == workingtemperature )
|
||
&& ( ( mvControlling->ScndPipePress > 4.5 ) || ( mvControlling->VeselVolume == 0.0 ) ) ) {
|
||
|
||
if( eStopReason == stopSleep ) {
|
||
// jeśli dotychczas spał teraz nie ma powodu do stania
|
||
eStopReason = stopNone;
|
||
}
|
||
eAction = TAction::actUnknown;
|
||
iEngineActive = 1;
|
||
iDrivigFlags |= moveActive; // może skanować sygnały i reagować na komendy
|
||
|
||
return true;
|
||
}
|
||
else {
|
||
iEngineActive = 0;
|
||
return false;
|
||
}
|
||
};
|
||
|
||
// wyłączanie silnika (test wyłączenia, a część wykonawcza tylko jeśli steruje komputer)
|
||
bool TController::ReleaseEngine() {
|
||
|
||
if( mvOccupied->Vel > 0.01 ) {
|
||
// TBD, TODO: make a dedicated braking procedure out of it for potential reuse
|
||
VelDesired = 0.0;
|
||
AccDesired = std::min( AccDesired, -1.25 ); // hamuj solidnie
|
||
ReactionTime = 0.1;
|
||
ZeroSpeed();
|
||
IncBrake();
|
||
// don't bother with the rest until we're standing still
|
||
return false;
|
||
}
|
||
|
||
LastReactionTime = 0.0;
|
||
ReactionTime = PrepareTime;
|
||
|
||
bool OK { false };
|
||
|
||
if( false == AIControllFlag ) {
|
||
// tylko to testujemy dla pojazdu człowieka
|
||
OK = ( ( mvOccupied->ActiveDir == 0 ) && ( mvControlling->Mains ) );
|
||
}
|
||
else {
|
||
// jeśli steruje komputer
|
||
mvOccupied->BrakeReleaser( 0 );
|
||
if( std::abs( fAccGravity ) < 0.01 ) {
|
||
// release train brake if on flats...
|
||
// TODO: check if we shouldn't leave it engaged instead
|
||
while( true == mvOccupied->DecBrakeLevel() ) {
|
||
// tu moze zmieniać na -2, ale to bez znaczenia
|
||
;
|
||
}
|
||
// ...and engage independent brake
|
||
while( true == mvOccupied->IncLocalBrakeLevel( 1 ) ) {
|
||
;
|
||
}
|
||
}
|
||
else {
|
||
// on slopes engage train brake
|
||
AccDesired = std::min( AccDesired, -0.9 );
|
||
while( true == IncBrake() ) {
|
||
;
|
||
}
|
||
}
|
||
ZeroSpeed();
|
||
ZeroDirection();
|
||
|
||
// zamykanie drzwi
|
||
mvOccupied->OperateDoors( side::right, false );
|
||
mvOccupied->OperateDoors( side::left, false );
|
||
|
||
if( true == mvControlling->Mains ) {
|
||
mvControlling->CompressorSwitch( false );
|
||
mvControlling->ConverterSwitch( false );
|
||
// line breaker/engine
|
||
OK = mvControlling->MainSwitch( false );
|
||
if( mvControlling->EnginePowerSource.SourceType == TPowerSource::CurrentCollector ) {
|
||
mvControlling->PantFront( false );
|
||
mvControlling->PantRear( false );
|
||
}
|
||
}
|
||
else {
|
||
OK = true;
|
||
}
|
||
|
||
if( OK ) {
|
||
// finish vehicle shutdown
|
||
if( ( mvControlling->EngineType == TEngineType::DieselElectric )
|
||
|| ( mvControlling->EngineType == TEngineType::DieselEngine ) ) {
|
||
// heating/cooling subsystem
|
||
mvControlling->WaterHeaterSwitch( false );
|
||
// optionally turn off the water pump as well
|
||
if( mvControlling->WaterPump.start_type != start_t::battery ) {
|
||
mvControlling->WaterPumpSwitch( false );
|
||
}
|
||
// fuel and oil subsystems
|
||
mvControlling->FuelPumpSwitch( false );
|
||
mvControlling->OilPumpSwitch( false );
|
||
}
|
||
// gasimy światła
|
||
Lights( 0, 0 );
|
||
mvOccupied->BatterySwitch( false );
|
||
}
|
||
}
|
||
|
||
if (OK) {
|
||
// jeśli się zatrzymał
|
||
iEngineActive = 0;
|
||
eStopReason = stopSleep; // stoimy z powodu wyłączenia
|
||
eAction = TAction::actSleep; //śpi (wygaszony)
|
||
|
||
OrderNext(Wait_for_orders); //żeby nie próbował coś robić dalej
|
||
iDrivigFlags &= ~moveActive; // ma nie skanować sygnałów i nie reagować na komendy
|
||
TableClear(); // zapominamy ograniczenia
|
||
VelSignalLast = -1.0;
|
||
}
|
||
return OK;
|
||
}
|
||
|
||
bool TController::IncBrake()
|
||
{ // zwiększenie hamowania
|
||
bool OK = false;
|
||
switch( mvOccupied->BrakeSystem ) {
|
||
case TBrakeSystem::Individual: {
|
||
if( mvOccupied->LocalBrake == TLocalBrake::ManualBrake ) {
|
||
OK = mvOccupied->IncManualBrakeLevel( 1 + static_cast<int>( std::floor( 0.5 + std::fabs( AccDesired ) ) ) );
|
||
}
|
||
else {
|
||
OK = mvOccupied->IncLocalBrakeLevel( std::floor( 1.5 + std::abs( AccDesired ) ) );
|
||
}
|
||
break;
|
||
}
|
||
case TBrakeSystem::Pneumatic: {
|
||
// NOTE: can't perform just test whether connected vehicle == nullptr, due to virtual couplers formed with nearby vehicles
|
||
bool standalone { true };
|
||
if( ( mvOccupied->TrainType == dt_ET41 )
|
||
|| ( mvOccupied->TrainType == dt_ET42 ) ) {
|
||
// NOTE: we're doing simplified checks full of presuptions here.
|
||
// they'll break if someone does strange thing like turning around the second unit
|
||
if( ( mvOccupied->Couplers[ end::rear ].CouplingFlag & coupling::permanent )
|
||
&& ( mvOccupied->Couplers[ end::rear ].Connected->Couplers[ end::rear ].Connected != nullptr ) ) {
|
||
standalone = false;
|
||
}
|
||
if( ( mvOccupied->Couplers[ end::front ].CouplingFlag & coupling::permanent )
|
||
&& ( mvOccupied->Couplers[ end::front ].Connected->Couplers[ end::front ].Connected != nullptr ) ) {
|
||
standalone = false;
|
||
}
|
||
}
|
||
else if( mvOccupied->TrainType == dt_DMU ) {
|
||
// enforce use of train brake for DMUs
|
||
standalone = false;
|
||
}
|
||
else if( ( ( OrderCurrentGet() & ( Loose_shunt ) ) != 0 )
|
||
&& ( mvOccupied->Vel - VelDesired < fVelPlus + fVelMinus ) ) {
|
||
// try to reduce train brake use in loose shunting mode
|
||
standalone = true;
|
||
}
|
||
else {
|
||
/*
|
||
standalone =
|
||
( ( mvOccupied->Couplers[ 0 ].CouplingFlag == 0 )
|
||
&& ( mvOccupied->Couplers[ 1 ].CouplingFlag == 0 ) );
|
||
*/
|
||
if( pVehicles[ end::front ] != pVehicles[ end::rear ] ) {
|
||
// more detailed version, will use manual braking also for coupled sets of controlled vehicles
|
||
auto *vehicle = pVehicles[ end::front ]; // start from first
|
||
while( ( true == standalone )
|
||
&& ( vehicle != nullptr ) ) {
|
||
// NOTE: we could simplify this by doing only check of the rear coupler, but this can be quite tricky in itself
|
||
// TODO: add easier ways to access front/rear coupler taking into account vehicle's direction
|
||
standalone =
|
||
( ( ( vehicle->MoverParameters->Couplers[ end::front ].Connected == nullptr ) || ( vehicle->MoverParameters->Couplers[ end::front ].CouplingFlag & coupling::control ) )
|
||
&& ( ( vehicle->MoverParameters->Couplers[ end::rear ].Connected == nullptr ) || ( vehicle->MoverParameters->Couplers[ end::rear ].CouplingFlag & coupling::control ) ) );
|
||
vehicle = vehicle->Next(); // kolejny pojazd, podłączony od tyłu (licząc od czoła)
|
||
}
|
||
}
|
||
}
|
||
|
||
//standalone = standalone && ( mvControlling->EIMCtrlType == 0 );
|
||
|
||
if( true == standalone ) {
|
||
if( mvControlling->EIMCtrlType > 0 ) {
|
||
OK = IncBrakeEIM();
|
||
}
|
||
else {
|
||
OK = mvOccupied->IncLocalBrakeLevel(
|
||
1 + static_cast<int>( std::floor( 0.5 + std::fabs( AccDesired ) ) ) ); // hamowanie lokalnym bo luzem jedzie
|
||
}
|
||
}
|
||
else {
|
||
if( /*GBH mvOccupied->BrakeCtrlPos*/ BrakeCtrlPosition + 1 == gbh_MAX ) {
|
||
if (AccDesired < -1.5) // hamowanie nagle
|
||
OK = /*mvOccupied->*/BrakeLevelAdd(1.0); //GBH
|
||
else
|
||
OK = false;
|
||
}
|
||
else {
|
||
// dodane dla towarowego
|
||
float pos_corr = 0;
|
||
|
||
TDynamicObject *d;
|
||
d = pVehicles[0]; // pojazd na czele składu
|
||
while (d)
|
||
{ // przeliczanie dodatkowego potrzebnego spadku ciśnienia
|
||
if( ( d->MoverParameters->Hamulec->GetBrakeStatus() & b_dmg ) == 0 ) {
|
||
pos_corr += ( d->MoverParameters->Hamulec->GetCRP() - 5.0 ) * d->MoverParameters->TotalMass;
|
||
}
|
||
d = d->Next(); // kolejny pojazd, podłączony od tyłu (licząc od czoła)
|
||
}
|
||
pos_corr = pos_corr / fMass * 2.5;
|
||
|
||
if (mvOccupied->BrakeHandle == TBrakeHandle::FV4a)
|
||
{
|
||
pos_corr += mvOccupied->Handle->GetCP()*0.2;
|
||
|
||
}
|
||
double deltaAcc = -AccDesired*BrakeAccFactor() - (fBrake_a0[0] + 4.0 * (/*GBH mvOccupied->BrakeCtrlPosR*/BrakeCtrlPosition - 1 - pos_corr)*fBrake_a1[0]);
|
||
|
||
if( deltaAcc > fBrake_a1[0])
|
||
{
|
||
if( /*GBH mvOccupied->BrakeCtrlPosR*/BrakeCtrlPosition < 0.1 ) {
|
||
OK = /*mvOccupied->*/BrakeLevelAdd( BrakingInitialLevel ); //GBH
|
||
// HACK: stronger braking to overcome SA134 engine behaviour
|
||
if( ( mvOccupied->TrainType == dt_DMU )
|
||
&& ( VelNext == 0.0 )
|
||
&& ( fBrakeDist < 200.0 ) ) {
|
||
BrakeLevelAdd(
|
||
fBrakeDist / ActualProximityDist < 0.8 ?
|
||
0.5 :
|
||
1.0 );
|
||
}
|
||
}
|
||
else
|
||
{
|
||
OK = /*mvOccupied->*/BrakeLevelAdd( BrakingLevelIncrease ); //GBH
|
||
// brake harder if the acceleration is much higher than desired
|
||
/*if( ( deltaAcc > 2 * fBrake_a1[ 0 ] )
|
||
&& ( mvOccupied->BrakeCtrlPosR + BrakingLevelIncrease <= 5.0 ) ) {
|
||
mvOccupied->BrakeLevelAdd( BrakingLevelIncrease );
|
||
} GBH */
|
||
if ((deltaAcc > 2 * fBrake_a1[0])
|
||
&& (BrakeCtrlPosition + BrakingLevelIncrease <= 5.0)) {
|
||
/*mvOccupied->*/BrakeLevelAdd(BrakingLevelIncrease);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
OK = false;
|
||
}
|
||
}
|
||
if( /*GBH mvOccupied->BrakeCtrlPos*/BrakeCtrlPosition > 0 ) {
|
||
if( mvOccupied->Hamulec->Releaser() ) {
|
||
mvOccupied->BrakeReleaser( 0 );
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case TBrakeSystem::ElectroPneumatic: {
|
||
if( mvOccupied->EngineType == TEngineType::ElectricInductionMotor ) {
|
||
if (mvOccupied->BrakeHandle == TBrakeHandle::MHZ_EN57) {
|
||
if (mvOccupied->BrakeCtrlPos < mvOccupied->Handle->GetPos(bh_FB))
|
||
OK = mvOccupied->BrakeLevelAdd(1.0);
|
||
}
|
||
else {
|
||
OK = IncBrakeEIM();
|
||
}
|
||
}
|
||
else 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;
|
||
break;
|
||
}
|
||
default: { break; }
|
||
}
|
||
return OK;
|
||
}
|
||
|
||
bool TController::IncBrakeEIM()
|
||
{ // zwiększenie hamowania
|
||
bool OK = false;
|
||
switch (mvOccupied->EIMCtrlType)
|
||
{
|
||
case 0:
|
||
OK = mvOccupied->IncLocalBrakeLevel(1);
|
||
break;
|
||
case 1:
|
||
OK = mvOccupied->MainCtrlPos > 0;
|
||
if (OK)
|
||
mvOccupied->MainCtrlPos = 0;
|
||
break;
|
||
case 2:
|
||
OK = mvOccupied->MainCtrlPos > 1;
|
||
if (OK)
|
||
mvOccupied->MainCtrlPos = 1;
|
||
break;
|
||
}
|
||
return OK;
|
||
}
|
||
|
||
bool TController::DecBrake()
|
||
{ // zmniejszenie siły hamowania
|
||
bool OK = false;
|
||
double deltaAcc = -1.0;
|
||
double pos_diff = 1.0;
|
||
switch (mvOccupied->BrakeSystem)
|
||
{
|
||
case TBrakeSystem::Individual:
|
||
if (mvOccupied->LocalBrake == TLocalBrake::ManualBrake)
|
||
OK = mvOccupied->DecManualBrakeLevel(1 + floor(0.5 + fabs(AccDesired)));
|
||
else
|
||
OK = mvOccupied->DecLocalBrakeLevel(1 + floor(0.5 + fabs(AccDesired)));
|
||
break;
|
||
case TBrakeSystem::Pneumatic:
|
||
if( ( fBrake_a0[ 0 ] != 0.0 )
|
||
|| ( fBrake_a1[ 0 ] != 0.0 ) ) {
|
||
if( mvOccupied->TrainType == dt_DMU )
|
||
pos_diff = 0.25;
|
||
deltaAcc = -AccDesired*BrakeAccFactor() - (fBrake_a0[0] + 4 * (/*GBH mvOccupied->BrakeCtrlPosR*/BrakeCtrlPosition - pos_diff)*fBrake_a1[0]);
|
||
}
|
||
if (deltaAcc < 0)
|
||
{
|
||
if (/*GBH mvOccupied->BrakeCtrlPosR*/BrakeCtrlPosition > 0)
|
||
{
|
||
OK = /*mvOccupied->*/BrakeLevelAdd(-0.25);
|
||
//if ((deltaAcc < 5 * fBrake_a1[0]) && (mvOccupied->BrakeCtrlPosR >= 1.2))
|
||
// mvOccupied->BrakeLevelAdd(-1.0);
|
||
/* if (mvOccupied->BrakeCtrlPosR < 0.74) GBH */
|
||
if (BrakeCtrlPosition < 0.74)
|
||
/*mvOccupied->*/BrakeLevelSet(gbh_RP);
|
||
}
|
||
}
|
||
if( !OK ) {
|
||
OK = mvOccupied->DecLocalBrakeLevel(2);
|
||
}
|
||
if (!OK) {
|
||
OK = DecBrakeEIM();
|
||
}
|
||
/*
|
||
// NOTE: disabled, duplicate of AI's behaviour in UpdateSituation()
|
||
if (mvOccupied->PipePress < 3.0)
|
||
Need_BrakeRelease = true;
|
||
*/
|
||
break;
|
||
case TBrakeSystem::ElectroPneumatic:
|
||
if (mvOccupied->EngineType == TEngineType::ElectricInductionMotor) {
|
||
if (mvOccupied->BrakeHandle == TBrakeHandle::MHZ_EN57) {
|
||
if (mvOccupied->BrakeCtrlPos > mvOccupied->Handle->GetPos(bh_RP))
|
||
OK = mvOccupied->BrakeLevelAdd(-1.0);
|
||
}
|
||
else {
|
||
OK = DecBrakeEIM();
|
||
}
|
||
}
|
||
else 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::DecBrakeEIM()
|
||
{ // zmniejszenie siły hamowania
|
||
bool OK = false;
|
||
switch (mvOccupied->EIMCtrlType)
|
||
{
|
||
case 0:
|
||
OK = mvOccupied->DecLocalBrakeLevel(1);
|
||
break;
|
||
case 1:
|
||
OK = mvOccupied->MainCtrlPos < 2;
|
||
if (OK)
|
||
mvOccupied->MainCtrlPos = 2;
|
||
break;
|
||
case 2:
|
||
OK = mvOccupied->MainCtrlPos < 3;
|
||
if (OK)
|
||
mvOccupied->MainCtrlPos = 3;
|
||
break;
|
||
}
|
||
return OK;
|
||
}
|
||
|
||
bool TController::IncSpeed()
|
||
{ // zwiększenie prędkości; zwraca false, jeśli dalej się nie da zwiększać
|
||
if( true == tsGuardSignal.is_playing() ) {
|
||
// jeśli gada, to nie jedziemy
|
||
return false;
|
||
}
|
||
bool OK = true;
|
||
if( ( iDrivigFlags & moveDoorOpened )
|
||
&& ( VelDesired > 0.0 ) ) { // to prevent door shuffle on stop
|
||
// zamykanie drzwi - tutaj wykonuje tylko AI (zmienia fActionTime)
|
||
Doors( false );
|
||
}
|
||
if( fActionTime < 0.0 ) {
|
||
// gdy jest nakaz poczekać z jazdą, to nie ruszać
|
||
return false;
|
||
}
|
||
if( true == mvOccupied->DepartureSignal ) {
|
||
// shut off departure warning
|
||
mvOccupied->signal_departure( false );
|
||
}
|
||
if (mvControlling->SlippingWheels)
|
||
return false; // jak poślizg, to nie przyspieszamy
|
||
switch (mvOccupied->EngineType)
|
||
{
|
||
case TEngineType::None: // McZapkie-041003: wagon sterowniczy
|
||
if (mvControlling->MainCtrlPosNo > 0) // jeśli ma czym kręcić
|
||
iDrivigFlags |= moveIncSpeed; // ustawienie flagi jazdy
|
||
return false;
|
||
case TEngineType::ElectricSeriesMotor:
|
||
if (mvControlling->EnginePowerSource.SourceType == TPowerSource::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->IsMainCtrlNoPowerPos()) ||
|
||
(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)) {
|
||
// use series mode:
|
||
// if high threshold is set for motor overload relay,
|
||
// if the power station is heavily burdened,
|
||
// if it generates enough traction force
|
||
// to build up speed to 30/40 km/h for passenger/cargo train (10 km/h less if going uphill)
|
||
auto const sufficienttractionforce { std::abs( mvControlling->Ft ) > ( IsHeavyCargoTrain ? 125 : 100 ) * 1000.0 };
|
||
auto const sufficientacceleration { AbsAccS_pub >= ( IsHeavyCargoTrain ? 0.02 : 0.04 ) };
|
||
auto const seriesmodefieldshunting { ( mvControlling->ScndCtrlPos > 0 ) && ( mvControlling->RList[ mvControlling->MainCtrlPos ].Bn == 1 ) };
|
||
auto const parallelmodefieldshunting { ( mvControlling->ScndCtrlPos > 0 ) && ( mvControlling->RList[ mvControlling->MainCtrlPos ].Bn > 1 ) };
|
||
auto const useseriesmodevoltage {
|
||
interpolate(
|
||
mvControlling->EnginePowerSource.CollectorParameters.MinV,
|
||
mvControlling->EnginePowerSource.CollectorParameters.MaxV,
|
||
( IsHeavyCargoTrain ? 0.35 : 0.40 ) ) };
|
||
auto const useseriesmode = (
|
||
( mvControlling->Imax > mvControlling->ImaxLo )
|
||
|| ( fVoltage < useseriesmodevoltage )
|
||
|| ( ( true == sufficienttractionforce )
|
||
&& ( true == sufficientacceleration )
|
||
&& ( mvOccupied->Vel <= ( IsCargoTrain ? 35 : 25 ) + ( seriesmodefieldshunting ? 5 : 0 ) - ( ( fAccGravity < -0.025 ) ? 10 : 0 ) ) ) );
|
||
// when not in series mode use the first available parallel mode configuration until 50/60 km/h for passenger/cargo train
|
||
// (if there's only one parallel mode configuration it'll be used regardless of current speed)
|
||
auto const usefieldshunting = (
|
||
( mvControlling->StLinFlag )
|
||
&& ( mvControlling->RList[ mvControlling->MainCtrlPos ].R < 0.01 )
|
||
&& ( useseriesmode ?
|
||
mvControlling->RList[ mvControlling->MainCtrlPos ].Bn == 1 :
|
||
( ( true == sufficienttractionforce )
|
||
&& ( true == sufficientacceleration )
|
||
&& ( mvOccupied->Vel <= ( IsCargoTrain ? 55 : 45 ) + ( parallelmodefieldshunting ? 5 : 0 ) ) ?
|
||
mvControlling->RList[ mvControlling->MainCtrlPos ].Bn > 1 :
|
||
mvControlling->MainCtrlPos == mvControlling->MainCtrlPosNo ) ) );
|
||
|
||
double Vs = 99999;
|
||
if( usefieldshunting ?
|
||
( mvControlling->ScndCtrlPos < mvControlling->ScndCtrlPosNo ) :
|
||
( mvControlling->MainCtrlPos < mvControlling->MainCtrlPosNo ) ) {
|
||
Vs = ESMVelocity( !usefieldshunting );
|
||
}
|
||
|
||
if( ( std::abs( mvControlling->Im ) < ( fReady < 0.4 ? mvControlling->Imin : mvControlling->IminLo ) )
|
||
|| ( mvControlling->Vel > Vs ) ) {
|
||
// Ra: wywalał nadmiarowy, bo Im może być ujemne; jak nie odhamowany, to nie przesadzać z prądem
|
||
if( usefieldshunting ) {
|
||
// to dać bocznik
|
||
// engage the shuntfield only if there's sufficient power margin to draw from
|
||
auto const sufficientpowermargin { fVoltage - useseriesmodevoltage > ( IsHeavyCargoTrain ? 100.0 : 75.0 ) };
|
||
|
||
OK = (
|
||
sufficientpowermargin ?
|
||
mvControlling->IncScndCtrl( 1 ) :
|
||
true );
|
||
}
|
||
else {
|
||
// jeśli ustawiony bocznik to bocznik na zero po chamsku
|
||
if( mvControlling->ScndCtrlPos ) {
|
||
mvControlling->DecScndCtrl( 2 );
|
||
}
|
||
// kręcimy nastawnik jazdy
|
||
// don't draw too much power;
|
||
// keep from dropping into series mode when entering/using parallel mode, and from shutting down in the series mode
|
||
auto const sufficientpowermargin {
|
||
fVoltage - (
|
||
mvControlling->RList[ std::min( mvControlling->MainCtrlPos + 1, mvControlling->MainCtrlPosNo ) ].Bn == 1 ?
|
||
mvControlling->EnginePowerSource.CollectorParameters.MinV :
|
||
useseriesmodevoltage )
|
||
> ( IsHeavyCargoTrain ? 80.0 : 60.0 ) };
|
||
|
||
OK = (
|
||
( sufficientpowermargin && ( false == mvControlling->DelayCtrlFlag ) ) ?
|
||
mvControlling->IncMainCtrl( 1 ) :
|
||
true );
|
||
// czekaj na 1 pozycji, zanim się nie włączą liniowe
|
||
if( true == mvControlling->StLinFlag ) {
|
||
iDrivigFlags |= moveIncSpeed;
|
||
}
|
||
else {
|
||
iDrivigFlags &= ~moveIncSpeed;
|
||
}
|
||
|
||
if( ( mvControlling->Im == 0 )
|
||
&& ( mvControlling->MainCtrlPowerPos() > 1 ) ) {
|
||
// brak prądu na dalszych pozycjach
|
||
// nie załączona lokomotywa albo wywalił nadmiarowy
|
||
Need_TryAgain = true;
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
OK = false;
|
||
}
|
||
}
|
||
mvControlling->AutoRelayCheck(); // sprawdzenie logiki sterowania
|
||
break;
|
||
case TEngineType::Dumb:
|
||
case TEngineType::DieselElectric:
|
||
if (!mvControlling->FuseFlag)
|
||
if (Ready || (iDrivigFlags & movePress)) //{(BrakePress<=0.01*MaxBrakePress)}
|
||
{
|
||
OK = mvControlling->IncMainCtrl(1);
|
||
if (!OK)
|
||
OK = mvControlling->IncScndCtrl(1);
|
||
}
|
||
break;
|
||
case TEngineType::ElectricInductionMotor:
|
||
if (!mvControlling->FuseFlag)
|
||
if (Ready || (iDrivigFlags & movePress) || (mvOccupied->ShuntMode)) //{(BrakePress<=0.01*MaxBrakePress)}
|
||
{
|
||
OK = IncSpeedEIM();
|
||
// cruise control
|
||
auto const SpeedCntrlVel { (
|
||
( ActualProximityDist > std::max( 50.0, fMaxProximityDist ) ) ?
|
||
VelDesired :
|
||
min_speed( VelDesired, VelNext ) ) };
|
||
SpeedCntrl(SpeedCntrlVel);
|
||
}
|
||
break;
|
||
case TEngineType::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 TEngineType::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( true == Ready ) {
|
||
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( false == mvControlling->Mains ) {
|
||
mvControlling->MainSwitch( true );
|
||
mvControlling->ConverterSwitch( true );
|
||
mvControlling->CompressorSwitch( true );
|
||
}
|
||
break;
|
||
}
|
||
return OK;
|
||
}
|
||
|
||
void TController::ZeroSpeed( bool const Enforce ) {
|
||
|
||
while( DecSpeed( Enforce ) ) {
|
||
;
|
||
}
|
||
}
|
||
|
||
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 TEngineType::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((mvControlling->MainCtrlPowerPos() > 1 ? 2 : 1));
|
||
mvControlling->AutoRelayCheck(); // sprawdzenie logiki sterowania
|
||
return false;
|
||
case TEngineType::ElectricSeriesMotor:
|
||
OK = mvControlling->DecScndCtrl(2); // najpierw bocznik na zero
|
||
if (!OK)
|
||
OK = mvControlling->DecMainCtrl((mvControlling->MainCtrlPowerPos() > 1 ? 2 : 1));
|
||
mvControlling->AutoRelayCheck(); // sprawdzenie logiki sterowania
|
||
break;
|
||
case TEngineType::Dumb:
|
||
case TEngineType::DieselElectric:
|
||
OK = mvControlling->DecScndCtrl(2);
|
||
if (!OK)
|
||
OK = mvControlling->DecMainCtrl(2 + (mvControlling->MainCtrlPowerPos() / 2));
|
||
break;
|
||
case TEngineType::ElectricInductionMotor:
|
||
OK = DecSpeedEIM();
|
||
break;
|
||
case TEngineType::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 TEngineType::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->MainCtrlPowerPos() > 1 ) ) {
|
||
OK = mvControlling->DecMainCtrl( 1 );
|
||
}
|
||
}
|
||
if (force) // przy aktywacji kabiny jest potrzeba natychmiastowego wyzerowania
|
||
OK = mvControlling->DecMainCtrl((mvControlling->MainCtrlPowerPos() > 2 ? 2 : 1));
|
||
break;
|
||
}
|
||
return OK;
|
||
};
|
||
|
||
bool TController::IncSpeedEIM() {
|
||
|
||
bool OK = false; // domyślnie false, aby wyszło z pętli while
|
||
switch( mvControlling->EIMCtrlType ) {
|
||
case 0:
|
||
OK = mvControlling->IncMainCtrl( std::max( 1, mvOccupied->MainCtrlPosNo / 10 ) );
|
||
break;
|
||
case 1:
|
||
OK = mvControlling->MainCtrlPos < 6;
|
||
if( OK )
|
||
mvControlling->MainCtrlPos = 6;
|
||
/*
|
||
// TBD, TODO: set position based on desired acceleration?
|
||
OK = mvControlling->MainCtrlPos < mvControlling->MainCtrlPosNo;
|
||
if( OK ) {
|
||
mvControlling->MainCtrlPos = clamp( mvControlling->MainCtrlPos + 1, 6, mvControlling->MainCtrlPosNo );
|
||
}
|
||
*/
|
||
break;
|
||
case 2:
|
||
OK = mvControlling->MainCtrlPos < 4;
|
||
if( OK )
|
||
mvControlling->MainCtrlPos = 4;
|
||
break;
|
||
}
|
||
return OK;
|
||
}
|
||
|
||
bool TController::DecSpeedEIM()
|
||
{ // zmniejszenie prędkości (ale nie hamowanie)
|
||
bool OK = false; // domyślnie false, aby wyszło z pętli while
|
||
switch (mvControlling->EIMCtrlType)
|
||
{
|
||
case 0:
|
||
OK = mvControlling->DecMainCtrl(1);
|
||
break;
|
||
case 1:
|
||
OK = mvControlling->MainCtrlPos > 4;
|
||
if (OK)
|
||
mvControlling->MainCtrlPos = 4;
|
||
break;
|
||
case 2:
|
||
OK = mvControlling->MainCtrlPos > 2;
|
||
if (OK)
|
||
mvControlling->MainCtrlPos = 2;
|
||
break;
|
||
}
|
||
return OK;
|
||
}
|
||
|
||
void TController::BrakeLevelSet(double b)
|
||
{ // ustawienie pozycji hamulca na wartość (b) w zakresie od -2 do BrakeCtrlPosNo
|
||
// jedyny dopuszczalny sposób przestawienia hamulca zasadniczego
|
||
if (BrakeCtrlPosition == b)
|
||
return; // nie przeliczać, jak nie ma zmiany
|
||
BrakeCtrlPosition = clamp(b, (double)gbh_MIN, (double)gbh_MAX);
|
||
}
|
||
|
||
bool TController::BrakeLevelAdd(double b)
|
||
{ // dodanie wartości (b) do pozycji hamulca (w tym ujemnej)
|
||
// zwraca false, gdy po dodaniu było by poza zakresem
|
||
BrakeLevelSet(BrakeCtrlPosition + b);
|
||
return b > 0.0 ? (BrakeCtrlPosition < gbh_MAX) :
|
||
(BrakeCtrlPosition > -1.0); // true, jeśli można kontynuować
|
||
}
|
||
|
||
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 TEngineType::None: // McZapkie-041003: wagon sterowniczy
|
||
if (mvControlling->MainCtrlPosNo > 0)
|
||
{ // jeśli ma czym kręcić
|
||
// TODO: sprawdzanie innego czlonu //if (!FuseFlagCheck())
|
||
if( ( iDrivigFlags & moveIncSpeed ) == 0 ) {
|
||
// przetok rozłączać od razu (no dependency on fActionTime)
|
||
while( ( mvControlling->MainCtrlPos )
|
||
&& ( mvControlling->DecMainCtrl( 1 ) ) ) {
|
||
; // na zero
|
||
}
|
||
if( fActionTime >= 0.0 ) {
|
||
fActionTime = -5.0; // niech trochę potrzyma
|
||
}
|
||
mvControlling->AutoRelayCheck(); // sprawdzenie logiki sterowania
|
||
}
|
||
else {
|
||
// jak ma jechać
|
||
if( fActionTime < 0.0 ) { break; }
|
||
if( fReady > 0.4 ) { break; }
|
||
|
||
if( mvOccupied->ActiveDir > 0 ) {
|
||
mvOccupied->DirectionForward(); //żeby EN57 jechały na drugiej nastawie
|
||
}
|
||
|
||
if( ( mvControlling->MainCtrlPos > 0 )
|
||
&& ( false == mvControlling->StLinFlag ) ) {
|
||
// jak niby jedzie, ale ma rozłączone liniowe to na zero i czekać na przewalenie kułakowego
|
||
mvControlling->DecMainCtrl( 2 );
|
||
}
|
||
else {
|
||
// ruch nastawnika uzależniony jest od aktualnie ustawionej pozycji
|
||
switch( mvControlling->MainCtrlPos ) {
|
||
case 0:
|
||
if( mvControlling->MainCtrlActualPos ) {
|
||
// jeśli kułakowy nie jest wyzerowany to czekać na wyzerowanie
|
||
break;
|
||
}
|
||
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
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
case TEngineType::ElectricSeriesMotor:
|
||
if( ( false == mvControlling->StLinFlag )
|
||
&& ( false == mvControlling->DelayCtrlFlag ) ) {
|
||
// styczniki liniowe rozłączone yBARC
|
||
ZeroSpeed();
|
||
}
|
||
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->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)
|
||
// NOTE: this step is likely to conflict with directive to operate sandbox based on the state of slipping wheels
|
||
// TODO: gather all sandbox operating logic in one place
|
||
if( fabs( mvControlling->Im ) > 0.75 * mvControlling->ImaxHi ) {
|
||
// jeśli prąd jest duży
|
||
mvControlling->Sandbox( true ); // piaskujemy tory, coby się nie ślizgać
|
||
}
|
||
else {
|
||
// otherwise we switch the sander off, if it's active
|
||
if( mvControlling->SandDose ) {
|
||
mvControlling->Sandbox( false );
|
||
}
|
||
}
|
||
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
|
||
}
|
||
break;
|
||
case TEngineType::Dumb:
|
||
case TEngineType::DieselElectric:
|
||
case TEngineType::ElectricInductionMotor:
|
||
break;
|
||
case TEngineType::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.75 * 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::SpeedCntrl(double DesiredSpeed)
|
||
{
|
||
if (mvControlling->ScndCtrlPosNo == 1)
|
||
{
|
||
mvControlling->IncScndCtrl(1);
|
||
mvControlling->RunCommand("SpeedCntrl", DesiredSpeed, mvControlling->CabNo);
|
||
}
|
||
else if ((mvControlling->ScndCtrlPosNo > 1) && (!mvOccupied->SpeedCtrlTypeTime))
|
||
{
|
||
int DesiredPos = 1 + mvControlling->ScndCtrlPosNo * ((DesiredSpeed - 1.0) / mvControlling->Vmax);
|
||
while( ( mvControlling->ScndCtrlPos > DesiredPos ) && ( true == mvControlling->DecScndCtrl( 1 ) ) ) { ; } // all work is done in the condition loop
|
||
while( ( mvControlling->ScndCtrlPos < DesiredPos ) && ( true == mvControlling->IncScndCtrl( 1 ) ) ) { ; } // all work is done in the condition loop
|
||
}
|
||
};
|
||
|
||
void TController::SetTimeControllers()
|
||
{
|
||
//1. Check the type of Main Brake Handle
|
||
if (mvOccupied->BrakeSystem == TBrakeSystem::Pneumatic)
|
||
{
|
||
if (mvOccupied->Handle->Time)
|
||
{
|
||
if ((BrakeCtrlPosition > 0) && (mvOccupied->Handle->GetCP() - 0.05 > mvOccupied->HighPipePress - BrakeCtrlPosition*0.25*mvOccupied->DeltaPipePress))
|
||
mvOccupied->BrakeLevelSet(mvOccupied->Handle->GetPos(bh_FB));
|
||
else if ((BrakeCtrlPosition > 0) && (mvOccupied->Handle->GetCP() + 0.05 < mvOccupied->HighPipePress - BrakeCtrlPosition*0.25*mvOccupied->DeltaPipePress))
|
||
mvOccupied->BrakeLevelSet(mvOccupied->Handle->GetPos(bh_RP));
|
||
else if (BrakeCtrlPosition == 0)
|
||
mvOccupied->BrakeLevelSet(mvOccupied->Handle->GetPos(bh_RP));
|
||
else if (BrakeCtrlPosition == -1)
|
||
mvOccupied->BrakeLevelSet(mvOccupied->Handle->GetPos(bh_FS));
|
||
else if (BrakeCtrlPosition == -2)
|
||
mvOccupied->BrakeLevelSet(mvOccupied->Handle->GetPos(bh_NP));
|
||
}
|
||
if (mvOccupied->BrakeHandle == TBrakeHandle::FV4a) mvOccupied->BrakeLevelSet(BrakeCtrlPosition);
|
||
if (mvOccupied->BrakeHandle == TBrakeHandle::MHZ_K8P)
|
||
{
|
||
if (BrakeCtrlPosition == 0)
|
||
mvOccupied->BrakeLevelSet(mvOccupied->Handle->GetPos(bh_RP));
|
||
else if (BrakeCtrlPosition == -1)
|
||
mvOccupied->BrakeLevelSet(mvOccupied->Handle->GetPos(bh_FS));
|
||
else if (BrakeCtrlPosition == -2)
|
||
mvOccupied->BrakeLevelSet(mvOccupied->Handle->GetPos(bh_NP));
|
||
else if (BrakeCtrlPosition > 4.5)
|
||
mvOccupied->BrakeLevelSet(10);
|
||
else if (BrakeCtrlPosition > 3.70)
|
||
mvOccupied->BrakeLevelSet(9);
|
||
else
|
||
mvOccupied->BrakeLevelSet(round((BrakeCtrlPosition * 0.4 - 0.1) / 0.15));
|
||
}
|
||
}
|
||
//2. Check the type of Secondary Brake Handle
|
||
|
||
//3. Check the type od EIMCtrlType
|
||
if (mvOccupied->EIMCtrlType > 0)
|
||
{
|
||
if (mvOccupied->EIMCtrlType == 1) //traxx
|
||
{
|
||
if (mvOccupied->LocalBrakePosA > 0.95) mvOccupied->MainCtrlPos = 0;
|
||
}
|
||
else if (mvOccupied->EIMCtrlType == 2) //elf
|
||
{
|
||
if (mvOccupied->LocalBrakePosA > 0.95) mvOccupied->MainCtrlPos = 1;
|
||
}
|
||
}
|
||
//4. Check Speed Control System
|
||
if (mvOccupied->EngineType == TEngineType::ElectricInductionMotor && mvOccupied->ScndCtrlPosNo > 1 && mvOccupied->SpeedCtrlTypeTime)
|
||
{
|
||
double SpeedCntrlVel =
|
||
(ActualProximityDist > std::max(50.0, fMaxProximityDist)) ?
|
||
VelDesired :
|
||
min_speed(VelDesired, VelNext);
|
||
SpeedCntrlVel = 10 * std::floor(SpeedCntrlVel*0.1);
|
||
if (mvOccupied->ScndCtrlPosNo == 4)
|
||
{
|
||
if (mvOccupied->NewSpeed + 0.1 < SpeedCntrlVel)
|
||
mvOccupied->ScndCtrlPos = 3;
|
||
if (mvOccupied->NewSpeed - 0.1 > SpeedCntrlVel)
|
||
mvOccupied->ScndCtrlPos = 1;
|
||
}
|
||
}
|
||
//5. Check Main Controller in Dizels
|
||
if ((mvControlling->EngineType == TEngineType::DieselEngine)&&(mvControlling->Vmax>30))
|
||
{
|
||
int MaxPos = mvControlling->MainCtrlPosNo;
|
||
int MinPos = MaxPos;
|
||
for (int i = MaxPos; (i > 1) && (mvControlling->RList[i].Mn > 0); i--) MinPos = i;
|
||
if ((MaxPos > MinPos)&&(mvControlling->MainCtrlPos>0)&&(AccDesired>0))
|
||
{
|
||
double Factor = 5 * (mvControlling->Vmax) / (mvControlling->Vmax + mvControlling->Vel);
|
||
int DesiredPos = MinPos + (MaxPos - MinPos)*(VelDesired > mvControlling->Vel ? (VelDesired - mvControlling->Vel) / Factor : 0);
|
||
if (DesiredPos > MaxPos) DesiredPos = MaxPos;
|
||
if (DesiredPos < MinPos) DesiredPos = MinPos;
|
||
if (!mvControlling->SlippingWheels)
|
||
{
|
||
while (mvControlling->MainCtrlPos > DesiredPos) mvControlling->DecMainCtrl(1);
|
||
if (mvControlling->Vel>mvControlling->dizel_minVelfullengage)
|
||
while (mvControlling->MainCtrlPos < DesiredPos) mvControlling->IncMainCtrl(1);
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
void TController::CheckTimeControllers()
|
||
{
|
||
//1. Check the type of Main Brake Handle
|
||
if (mvOccupied->BrakeSystem == TBrakeSystem::ElectroPneumatic && mvOccupied->Handle->TimeEP)
|
||
{
|
||
mvOccupied->BrakeLevelSet(mvOccupied->Handle->GetPos(bh_EPN));
|
||
}
|
||
if (mvOccupied->BrakeSystem == TBrakeSystem::Pneumatic && mvOccupied->Handle->Time)
|
||
{
|
||
if (BrakeCtrlPosition > 0)
|
||
mvOccupied->BrakeLevelSet(mvOccupied->Handle->GetPos(bh_MB));
|
||
else
|
||
mvOccupied->BrakeLevelSet(mvOccupied->Handle->GetPos(bh_RP));
|
||
}
|
||
//2. Check the type of Secondary Brake Handle
|
||
|
||
//3. Check the type od EIMCtrlType
|
||
if (mvOccupied->EIMCtrlType > 0)
|
||
{
|
||
if (mvOccupied->EIMCtrlType == 1) //traxx
|
||
{
|
||
if (mvOccupied->MainCtrlPos > 3) mvOccupied->MainCtrlPos = 5;
|
||
if (mvOccupied->MainCtrlPos < 3) mvOccupied->MainCtrlPos = 1;
|
||
}
|
||
else if (mvOccupied->EIMCtrlType == 2) //elf
|
||
{
|
||
if (mvOccupied->eimic > 0) mvOccupied->MainCtrlPos = 3;
|
||
if (mvOccupied->eimic < 0) mvOccupied->MainCtrlPos = 2;
|
||
}
|
||
}
|
||
//4. Check Speed Control System
|
||
if (mvOccupied->EngineType == TEngineType::ElectricInductionMotor && mvOccupied->ScndCtrlPosNo>1 && mvOccupied->SpeedCtrlTypeTime)
|
||
{
|
||
if (mvOccupied->ScndCtrlPosNo == 4)
|
||
{
|
||
mvOccupied->ScndCtrlPos = 2;
|
||
}
|
||
}
|
||
};
|
||
|
||
// otwieranie/zamykanie drzwi w składzie albo (tylko AI) EZT
|
||
void TController::Doors( bool const Open, int const Side ) {
|
||
|
||
if( true == Open ) {
|
||
// otwieranie drzwi
|
||
// otwieranie drzwi w składach wagonowych - docelowo wysyłać komendę zezwolenia na otwarcie drzwi
|
||
// tu będzie jeszcze długość peronu zaokrąglona do 10m (20m bezpieczniej, bo nie modyfikuje bitu 1)
|
||
auto const lewe = ( pVehicle->DirectionGet() > 0 ) ? 1 : 2;
|
||
auto const prawe = 3 - lewe;
|
||
|
||
if( ( true == pVehicle->MoverParameters->Doors.permit_needed )
|
||
&& ( true == AIControllFlag ) ) {
|
||
// grant door control permission if it's not automatic
|
||
// TBD: stricter requirements?
|
||
if( Side & prawe )
|
||
pVehicle->MoverParameters->PermitDoors( side::right );
|
||
if( Side & lewe )
|
||
pVehicle->MoverParameters->PermitDoors( side::left );
|
||
}
|
||
|
||
if( ( pVehicle->MoverParameters->Doors.open_control == control_t::conductor )
|
||
|| ( ( true == AIControllFlag )
|
||
&& ( ( pVehicle->MoverParameters->Doors.open_control == control_t::driver )
|
||
|| ( pVehicle->MoverParameters->Doors.open_control == control_t::mixed ) ) ) ) {
|
||
// if the door can be operated by the driver we let the user operate them unless this user is an ai
|
||
// the train conductor, if present, handles door operation also for human-driven trains
|
||
if( Side & prawe )
|
||
pVehicle->MoverParameters->OperateDoors( side::right, true );
|
||
if( Side & lewe )
|
||
pVehicle->MoverParameters->OperateDoors( side::left, true );
|
||
}
|
||
}
|
||
else {
|
||
// zamykanie
|
||
if( ( false == pVehicle->MoverParameters->Doors.permit_needed )
|
||
&& ( false == doors_open() ) ) {
|
||
// the doors are already closed and we don't have to revoke control permit, we can skip all hard work
|
||
iDrivigFlags &= ~moveDoorOpened;
|
||
}
|
||
|
||
if( AIControllFlag ) {
|
||
if( ( true == mvOccupied->Doors.has_warning )
|
||
&& ( false == mvOccupied->DepartureSignal )
|
||
&& ( true == TestFlag( iDrivigFlags, moveDoorOpened ) ) ) {
|
||
mvOccupied->signal_departure( true ); // załącenie bzyczka
|
||
fActionTime = -1.5 - 0.1 * Random( 10 ); // 1.5-2.5 second wait
|
||
}
|
||
}
|
||
|
||
if( ( true == TestFlag( iDrivigFlags, moveDoorOpened ) )
|
||
&& ( ( fActionTime > -0.5 )
|
||
|| ( false == AIControllFlag ) ) ) {
|
||
// ai doesn't close the door until it's free to depart, but human driver has free reign to do stupid things
|
||
if( ( pVehicle->MoverParameters->Doors.close_control == control_t::conductor )
|
||
|| ( ( true == AIControllFlag ) ) ) {
|
||
// if the door are controlled by the driver, we let the user operate them unless this user is an ai
|
||
// the train conductor, if present, handles door operation also for human-driven trains
|
||
if( ( pVehicle->MoverParameters->Doors.close_control == control_t::driver )
|
||
|| ( pVehicle->MoverParameters->Doors.close_control == control_t::mixed ) ) {
|
||
pVehicle->MoverParameters->OperateDoors( side::right, false );
|
||
pVehicle->MoverParameters->OperateDoors( side::left, false );
|
||
}
|
||
if( pVehicle->MoverParameters->Doors.permit_needed ) {
|
||
pVehicle->MoverParameters->PermitDoors( side::right, false );
|
||
pVehicle->MoverParameters->PermitDoors( side::left, false );
|
||
}
|
||
}
|
||
|
||
auto *vehicle = pVehicles[ 0 ]; // pojazd na czole składu
|
||
while( vehicle != nullptr ) {
|
||
// zamykanie drzwi w pojazdach - flaga zezwolenia była by lepsza
|
||
if( vehicle->MoverParameters->Doors.auto_velocity < 0.f ) {
|
||
vehicle->MoverParameters->OperateDoors( side::right, false, range_t::local ); // w lokomotywie można by nie zamykać...
|
||
vehicle->MoverParameters->OperateDoors( side::left, false, range_t::local );
|
||
}
|
||
vehicle = vehicle->Next(); // pojazd podłączony z tyłu (patrząc od czoła)
|
||
}
|
||
fActionTime = -2.0 - 0.1 * Random( 15 ); // 2.0-3.5 sec wait, +potentially 0.5 remaining
|
||
iDrivigFlags &= ~moveDoorOpened; // zostały zamknięte - nie wykonywać drugi raz
|
||
|
||
if( Random( 100 ) < Random( 100 ) ) {
|
||
// potentially turn off the warning before actual departure
|
||
// TBD, TODO: dedicated buzzer duration counter
|
||
mvOccupied->signal_departure( false );
|
||
}
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
// returns true if any vehicle in the consist has open doors
|
||
bool
|
||
TController::doors_open() const {
|
||
|
||
return (
|
||
IsAnyDoorOpen[ side::right ]
|
||
|| IsAnyDoorOpen[ side::left ] );
|
||
/*
|
||
auto *vehicle = pVehicles[ 0 ]; // pojazd na czole składu
|
||
while( vehicle != nullptr ) {
|
||
if( ( false == vehicle->MoverParameters->Doors.instances[side::right].is_closed )
|
||
|| ( false == vehicle->MoverParameters->Doors.instances[side::left].is_closed ) ) {
|
||
// any open door is enough
|
||
return true;
|
||
}
|
||
vehicle = vehicle->Next();
|
||
}
|
||
// if we're still here there's nothing open
|
||
return false;
|
||
*/
|
||
}
|
||
|
||
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(std::string 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
|
||
// zamiana na współrzędne scenerii
|
||
glm::dvec3 sl { -NewLocation.X, NewLocation.Z, NewLocation.Y };
|
||
if (!PutCommand(NewCommand, NewValue1, NewValue2, &sl, reason))
|
||
mvOccupied->PutCommand(NewCommand, NewValue1, NewValue2, NewLocation);
|
||
}
|
||
|
||
bool TController::PutCommand( std::string NewCommand, double NewValue1, double NewValue2, glm::dvec3 const *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
|
||
}
|
||
|
||
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
|
||
}
|
||
|
||
if (NewCommand.compare(0, 10, "Timetable:") == 0)
|
||
{ // przypisanie nowego rozkładu jazdy, również prowadzonemu przez użytkownika
|
||
NewCommand.erase(0, 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
|
||
tsGuardSignal = sound_source { sound_placement::internal, 2 * EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // wywalenie kierownika
|
||
if (NewCommand != "none")
|
||
{
|
||
if (!TrainParams->LoadTTfile(
|
||
std::string(Global.asCurrentSceneryPath.c_str()), 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 " +
|
||
std::to_string(ConversionError) + " in position " + std::to_string(TrainParams->StationCount));
|
||
NewCommand = ""; // puste, dla wymiennej tekstury
|
||
}
|
||
else
|
||
{ // inicjacja pierwszego przystanku i pobranie jego nazwy
|
||
// HACK: clear the potentially set door state flag to ensure door get opened if applicable
|
||
iDrivigFlags &= ~( moveDoorOpened );
|
||
|
||
TrainParams->UpdateMTable( simulation::Time, 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;
|
||
auto lookup =
|
||
FileExists(
|
||
{ Global.asCurrentSceneryPath + NewCommand, szSoundPath + NewCommand },
|
||
{ ".ogg", ".flac", ".wav" } );
|
||
if( false == lookup.first.empty() ) {
|
||
// wczytanie dźwięku odjazdu podawanego bezpośrenido
|
||
tsGuardSignal = sound_source( sound_placement::external, 75.f ).deserialize( lookup.first + lookup.second, sound_type::single );
|
||
iGuardRadio = 0; // nie przez radio
|
||
}
|
||
else {
|
||
NewCommand += "radio";
|
||
auto lookup =
|
||
FileExists(
|
||
{ Global.asCurrentSceneryPath + NewCommand, szSoundPath + NewCommand },
|
||
{ ".ogg", ".flac", ".wav" } );
|
||
if( false == lookup.first.empty() ) {
|
||
// wczytanie dźwięku odjazdu w wersji radiowej (słychać tylko w kabinie)
|
||
tsGuardSignal = sound_source( sound_placement::internal, 2 * EU07_SOUND_CABCONTROLSCUTOFFRANGE ).deserialize( lookup.first + lookup.second, sound_type::single );
|
||
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, TrainParams->TrainName); // 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)
|
||
{
|
||
auto v = *NewLocation - pVehicle->GetPosition(); // wektor do punktu sterującego
|
||
auto 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) && (OrderCurrentGet() != Obey_train))
|
||
{ // o ile jazda
|
||
if( iEngineActive == 0 ) {
|
||
// trzeba odpalić silnik najpierw, światła ustawi
|
||
OrderNext( Prepare_engine );
|
||
}
|
||
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
|
||
return true;
|
||
}
|
||
|
||
if (NewCommand == "SetProximityVelocity")
|
||
{
|
||
/*
|
||
if (SetProximityVelocity(NewValue1,NewValue2))
|
||
if (NewLocation)
|
||
vCommandLocation=*NewLocation;
|
||
*/
|
||
return true;
|
||
}
|
||
|
||
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
|
||
|
||
return true;
|
||
}
|
||
|
||
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)
|
||
|
||
return true;
|
||
}
|
||
|
||
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
|
||
return true;
|
||
}
|
||
|
||
if (NewCommand == "Change_direction")
|
||
{
|
||
TOrders o = OrderCurrentGet(); // 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)
|
||
{
|
||
auto v = *NewLocation - pVehicle->GetPosition(); // wektor do punktu sterującego
|
||
auto 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
|
||
return true;
|
||
}
|
||
|
||
if (NewCommand == "Obey_train") {
|
||
if( false == 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
|
||
|
||
return true;
|
||
}
|
||
|
||
if (NewCommand == "Bank") {
|
||
if( false == iEngineActive ) {
|
||
OrderNext( Prepare_engine ); // trzeba odpalić silnik najpierw
|
||
}
|
||
OrderNext( Bank );
|
||
// if (NewValue1>0) TrainNumber=floor(NewValue1); //i co potem ???
|
||
OrderCheck(); // jeśli jazda pociągowa teraz, to wykonać niezbędne operacje
|
||
|
||
return true;
|
||
}
|
||
|
||
if( ( NewCommand == "Shunt" ) || ( NewCommand == "Loose_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
|
||
// x,-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
|
||
if( ( NewValue2 > 0.0 )
|
||
&& ( NewCommand == "Loose_shunt" ) ) {
|
||
// after decoupling continue pushing in the original direction
|
||
// NOTE: for backward compatibility this option isn't supported for basic shunting mode
|
||
iDirectionOrder = iDirection; // back to pushing
|
||
OrderPush( Change_direction );
|
||
}
|
||
}
|
||
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 ? end::rear : end::front ].Connected == nullptr ) // z tyłu nic
|
||
&& ( mvOccupied->Couplers[ mvOccupied->DirAbsolute > 0 ? end::front : end::rear ].Connected != nullptr ) ) { // 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 ? end::rear : end::front ].Connected != nullptr ) { // 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(( NewCommand == "Shunt" ? Obey_train : Bank )); // to potem jazda pociągowa
|
||
else
|
||
OrderNext(( NewCommand == "Shunt" ? Shunt : Loose_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
|
||
*/
|
||
return true;
|
||
}
|
||
|
||
if( NewCommand == "Jump_to_first_order" ) {
|
||
JumpToFirstOrder();
|
||
return true;
|
||
}
|
||
|
||
if (NewCommand == "Jump_to_order")
|
||
{
|
||
if( NewValue1 == -1.0 ) {
|
||
JumpToNextOrder();
|
||
}
|
||
else if ((NewValue1 >= 0) && (NewValue1 < maxorders))
|
||
{
|
||
OrderPos = floor(NewValue1);
|
||
if( !OrderPos ) {
|
||
// zgodność wstecz: dopiero pierwsza uruchamia
|
||
OrderPos = 1;
|
||
}
|
||
#if LOGORDERS
|
||
WriteLog("--> Jump_to_order");
|
||
OrdersDump();
|
||
#endif
|
||
}
|
||
return true;
|
||
}
|
||
|
||
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; // horn combination flag
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
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 ) {
|
||
// kierownikowi też zmienić
|
||
iGuardRadio = iRadioChannel;
|
||
}
|
||
}
|
||
// NewValue2 może zawierać dodatkowo oczekiwany kod odpowiedzi, np. dla W29 "nawiązać
|
||
// łączność radiową z dyżurnym ruchu odcinkowym"
|
||
return true;
|
||
}
|
||
|
||
if( NewCommand == "SetLights" ) {
|
||
// set consist lights pattern hints
|
||
m_lighthints[ end::front ] = static_cast<int>( NewValue1 );
|
||
m_lighthints[ end::rear ] = static_cast<int>( NewValue2 );
|
||
if( true == TestFlag( OrderCurrentGet(), Obey_train ) ) {
|
||
// light hints only apply in the obey_train mode
|
||
CheckVehicles();
|
||
}
|
||
return true;
|
||
}
|
||
|
||
return false; // nierozpoznana - wysłać bezpośrednio do pojazdu
|
||
};
|
||
|
||
void TController::PhysicsLog()
|
||
{ // zapis logu - na razie tylko wypisanie parametrów
|
||
if (LogFile.is_open())
|
||
{
|
||
#if LOGPRESS == 0
|
||
/*
|
||
<< "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 "
|
||
<< "EngineTemp[Deg] OilTemp[Deg] WaterTemp[Deg] WaterAuxTemp[Deg]"
|
||
*/
|
||
LogFile
|
||
<< ElapsedTime << " "
|
||
<< fabs(11.31 * mvOccupied->WheelDiameter * mvOccupied->nrot) << " "
|
||
<< mvControlling->AccS << " "
|
||
<< mvOccupied->Couplers[1].Dist << " "
|
||
<< mvOccupied->Couplers[1].CForce << " "
|
||
<< mvOccupied->Ft << " "
|
||
<< mvOccupied->Ff << " "
|
||
<< mvOccupied->Fb << " "
|
||
<< mvOccupied->BrakePress << " "
|
||
<< mvOccupied->PipePress << " "
|
||
<< mvControlling->Im << " "
|
||
<< int(mvControlling->MainCtrlPos) << " "
|
||
<< int(mvControlling->ScndCtrlPos) << " "
|
||
<< mvOccupied->fBrakeCtrlPos << " "
|
||
<< mvOccupied->LocalBrakePosA << " "
|
||
<< int(mvControlling->ActiveDir) << " "
|
||
<< ( mvOccupied->CommandIn.Command.empty() ? "none" : mvOccupied->CommandIn.Command.c_str() ) << " "
|
||
<< mvOccupied->CommandIn.Value1 << " "
|
||
<< mvOccupied->CommandIn.Value2 << " "
|
||
<< int(mvControlling->SecuritySystem.Status) << " "
|
||
<< int(mvControlling->SlippingWheels) << " "
|
||
<< mvControlling->dizel_heat.Ts << " "
|
||
<< mvControlling->dizel_heat.To << " "
|
||
<< mvControlling->dizel_heat.temperatura1 << " "
|
||
<< mvControlling->dizel_heat.temperatura2
|
||
<< "\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();
|
||
}
|
||
};
|
||
|
||
void
|
||
TController::UpdateSituation(double dt) {
|
||
// uruchamiać przynajmniej raz na sekundę
|
||
if( ( iDrivigFlags & movePrimary ) == 0 ) { return; } // pasywny nic nie robi
|
||
|
||
// update timers
|
||
ElapsedTime += dt;
|
||
WaitingTime += dt;
|
||
fBrakeTime -= dt; // wpisana wartość jest zmniejszana do 0, gdy ujemna należy zmienić nastawę hamulca
|
||
if( mvOccupied->fBrakeCtrlPos != mvOccupied->Handle->GetPos( bh_FS ) ) {
|
||
// brake charging timeout starts after charging ends
|
||
BrakeChargingCooldown += dt;
|
||
}
|
||
fStopTime += dt; // zliczanie czasu postoju, nie ruszy dopóki ujemne
|
||
fActionTime += dt; // czas używany przy regulacji prędkości i zamykaniu drzwi
|
||
LastReactionTime += dt;
|
||
LastUpdatedTime += dt;
|
||
if( ( mvOccupied->Vel < 0.05 )
|
||
&& ( ( OrderCurrentGet() & ( Shunt | Loose_shunt | Obey_train | Bank ) ) != 0 ) ) {
|
||
IdleTime += dt;
|
||
}
|
||
else {
|
||
IdleTime = 0.0;
|
||
}
|
||
// HACK: activate route scanning if an idling vehicle is activated by a human user
|
||
if( ( OrderCurrentGet() == Wait_for_orders )
|
||
&& ( false == AIControllFlag )
|
||
&& ( false == iEngineActive )
|
||
&& ( true == mvControlling->Battery ) ) {
|
||
OrderNext( Prepare_engine );
|
||
}
|
||
|
||
// log vehicle data
|
||
if( ( WriteLogFlag )
|
||
&& ( LastUpdatedTime > deltalog ) ) {
|
||
// zapis do pliku DAT
|
||
PhysicsLog();
|
||
LastUpdatedTime -= deltalog;
|
||
}
|
||
|
||
if( ( fLastStopExpDist > 0.0 )
|
||
&& ( mvOccupied->DistCounter > fLastStopExpDist ) ) {
|
||
// zaktualizować wyświetlanie rozkładu
|
||
iStationStart = TrainParams->StationIndex;
|
||
fLastStopExpDist = -1.0; // usunąć licznik
|
||
}
|
||
|
||
// ABu-160305 testowanie gotowości do jazdy
|
||
// Ra: przeniesione z DynObj, skład użytkownika też jest testowany, żeby mu przekazać, że ma odhamować
|
||
int index = double(BrakeAccTableSize) * (mvOccupied->Vel / mvOccupied->Vmax);
|
||
index = std::min(BrakeAccTableSize, std::max(1, index));
|
||
fBrake_a0[0] = fBrake_a0[index];
|
||
fBrake_a1[0] = fBrake_a1[index];
|
||
|
||
if ((mvOccupied->TrainType == dt_EZT) || (mvOccupied->TrainType == dt_DMU)) {
|
||
auto Coeff = clamp( mvOccupied->Vel*0.015 , 0.5 , 1.0);
|
||
fAccThreshold = fNominalAccThreshold * Coeff - fBrake_a0[BrakeAccTableSize] * (1.0 - Coeff);
|
||
}
|
||
|
||
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>
|
||
double AbsAccS = 0;
|
||
IsAnyDoorOpen[ side::right ] = IsAnyDoorOpen[ side::left ] = false;
|
||
TDynamicObject *p = pVehicles[0]; // pojazd na czole składu
|
||
while (p)
|
||
{ // sprawdzenie odhamowania wszystkich połączonych pojazdów
|
||
auto *vehicle { p->MoverParameters };
|
||
if (Ready) {
|
||
// bo jak coś nie odhamowane, to dalej nie ma co sprawdzać
|
||
if (vehicle->BrakePress >= 0.4) // wg UIC określone sztywno na 0.04
|
||
{
|
||
Ready = false; // nie gotowy
|
||
// Ra: odluźnianie przeładowanych lokomotyw, ciągniętych na zimno - prowizorka...
|
||
if (AIControllFlag) // skład jak dotąd był wyluzowany
|
||
{
|
||
if( ( /*GBH mvOccupied->BrakeCtrlPos*/BrakeCtrlPosition == 0 ) // jest pozycja jazdy
|
||
&& ( ( vehicle->Hamulec->GetBrakeStatus() & b_dmg ) == 0 ) // brake isn't broken
|
||
&& ( vehicle->PipePress - 5.0 > -0.1 ) // jeśli ciśnienie jak dla jazdy
|
||
&& ( vehicle->Hamulec->GetCRP() > vehicle->PipePress + 0.12 ) ) { // za dużo w zbiorniku
|
||
// indywidualne luzowanko
|
||
vehicle->BrakeReleaser( 1 );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (fReady < vehicle->BrakePress)
|
||
fReady = vehicle->BrakePress; // szukanie najbardziej zahamowanego
|
||
if( ( dy = p->VectorFront().y ) != 0.0 ) {
|
||
// istotne tylko dla pojazdów na pochyleniu
|
||
// ciężar razy składowa styczna grawitacji
|
||
fAccGravity -= vehicle->TotalMassxg * dy * ( p->DirectionGet() == iDirection ? 1 : -1 );
|
||
}
|
||
if( ( vehicle->Power > 0.01 ) // jeśli ma silnik
|
||
&& ( vehicle->FuseFlag ) ) { // wywalony nadmiarowy
|
||
Need_TryAgain = true; // reset jak przy wywaleniu nadmiarowego
|
||
}
|
||
// check door state
|
||
auto const switchsides { p->DirectionGet() != iDirection };
|
||
IsAnyDoorOpen[ side::right ] =
|
||
IsAnyDoorOpen[ side::right ]
|
||
|| ( false == vehicle->Doors.instances[ ( switchsides ? side::left : side::right ) ].is_closed );
|
||
IsAnyDoorOpen[ side::left ] =
|
||
IsAnyDoorOpen[ side::left ]
|
||
|| ( false == vehicle->Doors.instances[ ( switchsides ? side::right : side::left ) ].is_closed );
|
||
|
||
p = p->Next(); // pojazd podłączony z tyłu (patrząc od czoła)
|
||
}
|
||
|
||
// test state of main switch in all powered vehicles under control
|
||
IsLineBreakerClosed = ( mvOccupied->Power > 0.01 ? mvOccupied->Mains : true );
|
||
p = pVehicle;
|
||
while( ( true == IsLineBreakerClosed )
|
||
&& ( ( p = p->PrevC( coupling::control) ) != nullptr ) ) {
|
||
auto const *vehicle { p->MoverParameters };
|
||
if( vehicle->Power > 0.01 ) {
|
||
IsLineBreakerClosed = ( IsLineBreakerClosed && vehicle->Mains );
|
||
}
|
||
}
|
||
p = pVehicle;
|
||
while( ( true == IsLineBreakerClosed )
|
||
&& ( ( p = p->NextC( coupling::control ) ) != nullptr ) ) {
|
||
auto const *vehicle { p->MoverParameters };
|
||
if( vehicle->Power > 0.01 ) {
|
||
IsLineBreakerClosed = ( IsLineBreakerClosed && vehicle->Mains );
|
||
}
|
||
}
|
||
|
||
if( iDirection ) {
|
||
// siłę generują pojazdy na pochyleniu ale działa ona całość składu, więc a=F/m
|
||
fAccGravity *= iDirection;
|
||
fAccGravity /= fMass;
|
||
}
|
||
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
|
||
// second pass, for diesel engines verify the (live) engines are fully started
|
||
// TODO: cache presence of diesel engines in the consist, to skip this test if there isn't any
|
||
p = pVehicles[ 0 ]; // pojazd na czole składu
|
||
while( ( true == Ready )
|
||
&& ( p != nullptr ) ) {
|
||
|
||
auto const *vehicle { p->MoverParameters };
|
||
|
||
if( ( vehicle->EngineType == TEngineType::DieselEngine )
|
||
|| ( vehicle->EngineType == TEngineType::DieselElectric ) ) {
|
||
|
||
Ready = (
|
||
( vehicle->Vel > 0.5 ) // already moving
|
||
|| ( false == vehicle->Mains ) // deadweight vehicle
|
||
|| ( vehicle->enrot > 0.8 * (
|
||
vehicle->EngineType == TEngineType::DieselEngine ?
|
||
vehicle->dizel_nmin :
|
||
vehicle->DElist[ 0 ].RPM / 60.0 ) ) );
|
||
}
|
||
p = p->Next(); // pojazd podłączony z tyłu (patrząc od czoła)
|
||
}
|
||
|
||
// HACK: crude way to deal with automatic door opening on W4 preventing further ride
|
||
// for human-controlled vehicles with no door control and dynamic brake auto-activating with door open
|
||
// TODO: check if this situation still happens and the hack is still needed
|
||
if( ( false == AIControllFlag )
|
||
&& ( iDrivigFlags & moveDoorOpened )
|
||
&& ( mvOccupied->Doors.close_control != control_t::driver ) ) {
|
||
// for diesel engines react when engine is put past idle revolutions
|
||
// for others straightforward master controller check
|
||
if( ( mvControlling->EngineType == TEngineType::DieselEngine ?
|
||
mvControlling->RList[ mvControlling->MainCtrlPos ].Mn > 0 :
|
||
mvControlling->MainCtrlPowerPos() > 0 ) ) {
|
||
Doors( false );
|
||
}
|
||
}
|
||
|
||
// basic situational ai operations
|
||
// TBD, TODO: move these to main routine, if it's not neccessary for them to fire every time?
|
||
if( AIControllFlag ) {
|
||
|
||
// wheel slip
|
||
if( mvControlling->SlippingWheels ) {
|
||
mvControlling->Sandbox( true ); // piasku!
|
||
}
|
||
else {
|
||
// deactivate sandbox if we aren't slipping
|
||
if( mvControlling->SandDose ) {
|
||
mvControlling->Sandbox( false );
|
||
}
|
||
}
|
||
|
||
if (mvControlling->EnginePowerSource.SourceType == TPowerSource::CurrentCollector) {
|
||
|
||
if( mvOccupied->ScndPipePress > 4.3 ) {
|
||
// gdy główna sprężarka bezpiecznie nabije ciśnienie to można przestawić kurek na zasilanie pantografów z głównej pneumatyki
|
||
mvControlling->bPantKurek3 = true;
|
||
}
|
||
|
||
// uśrednione napięcie sieci: przy spadku poniżej wartości minimalnej opóźnić rozruch o losowy czas
|
||
fVoltage = 0.5 * (fVoltage + std::abs(mvControlling->RunningTraction.TractionVoltage));
|
||
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, losowy czas oczekiwania przed ponownym załączeniem jazdy
|
||
fActionTime = -2.0 - Random( 10 );
|
||
}
|
||
}
|
||
}
|
||
|
||
if( mvOccupied->Vel > 1.0 ) {
|
||
// jeżeli jedzie
|
||
if( iDrivigFlags & moveDoorOpened ) {
|
||
// jeśli drzwi otwarte
|
||
// nie zamykać drzwi przy drganiach, bo zatrzymanie na W4 akceptuje niewielkie prędkości
|
||
Doors( false );
|
||
}
|
||
/*
|
||
// NOTE: this section moved all cars to the edge of their respective roads
|
||
// it may have some use eventually for collision avoidance,
|
||
// but when enabled all the time it produces silly effect
|
||
// 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 == TPowerSource::CurrentCollector) {
|
||
|
||
if( mvOccupied->Vel > 0.05 ) {
|
||
// is moving
|
||
if( ( fOverhead2 >= 0.0 ) || iOverheadZero ) {
|
||
// jeśli jazda bezprądowa albo z opuszczonym pantografem
|
||
ZeroSpeed();
|
||
}
|
||
if( ( fOverhead2 > 0.0 ) || iOverheadDown ) {
|
||
// jazda z opuszczonymi pantografami
|
||
mvControlling->PantFront( false );
|
||
mvControlling->PantRear( false );
|
||
}
|
||
else {
|
||
// jeśli nie trzeba opuszczać pantografów
|
||
// jazda na tylnym
|
||
if( iDirection >= 0 ) {
|
||
// jak jedzie w kierunku sprzęgu 0
|
||
mvControlling->PantRear( true );
|
||
}
|
||
else {
|
||
mvControlling->PantFront( true );
|
||
}
|
||
}
|
||
if( mvOccupied->Vel > 5 ) {
|
||
// opuszczenie przedniego po rozpędzeniu się o ile jest więcej niż jeden
|
||
if( mvControlling->EnginePowerSource.CollectorParameters.CollectorsNo > 1 ) {
|
||
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
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// TODO: refactor this calculation into a subroutine
|
||
auto const useseriesmodevoltage {
|
||
interpolate(
|
||
mvControlling->EnginePowerSource.CollectorParameters.MinV,
|
||
mvControlling->EnginePowerSource.CollectorParameters.MaxV,
|
||
( IsHeavyCargoTrain ? 0.35 : 0.40 ) ) };
|
||
|
||
if( fVoltage <= useseriesmodevoltage ) {
|
||
// if the power station is heavily burdened try to reduce the load
|
||
switch( mvControlling->EngineType ) {
|
||
|
||
case TEngineType::ElectricSeriesMotor: {
|
||
if( mvControlling->RList[ mvControlling->MainCtrlPos ].Bn > 1 ) {
|
||
// limit yourself to series mode
|
||
if( mvControlling->ScndCtrlPos ) {
|
||
mvControlling->DecScndCtrl( 2 );
|
||
}
|
||
while( ( mvControlling->RList[ mvControlling->MainCtrlPos ].Bn > 1 )
|
||
&& ( mvControlling->DecMainCtrl( 1 ) ) ) {
|
||
; // all work is performed in the header
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
default: {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
if( ( IdleTime > 45.0 )
|
||
// NOTE: abs(stoptime) covers either at least 15 sec remaining for a scheduled stop, or 15+ secs spent at a basic stop
|
||
&& ( std::abs( fStopTime ) > 15.0 ) ) {
|
||
// spending a longer at a stop, raise also front pantograph
|
||
if( iDirection >= 0 ) // jak jedzie w kierunku sprzęgu 0
|
||
mvControlling->PantFront( true );
|
||
else
|
||
mvControlling->PantRear( true );
|
||
}
|
||
}
|
||
}
|
||
|
||
// horn control
|
||
if( fWarningDuration > 0.0 ) {
|
||
// jeśli pozostało coś do wytrąbienia trąbienie trwa nadal
|
||
fWarningDuration -= dt;
|
||
if( fWarningDuration < 0.05 )
|
||
mvOccupied->WarningSignal = 0; // a tu się kończy
|
||
}
|
||
if( mvOccupied->Vel >= 5.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
|
||
}
|
||
|
||
if( ( true == TestFlag( iDrivigFlags, moveStartHornNow ) )
|
||
&& ( true == Ready )
|
||
&& ( iEngineActive != 0 )
|
||
&& ( mvControlling->MainCtrlPowerPos() > 0 ) ) {
|
||
// uruchomienie trąbienia przed ruszeniem
|
||
fWarningDuration = 0.3; // czas trąbienia
|
||
mvOccupied->WarningSignal = pVehicle->iHornWarning; // wysokość tonu (2=wysoki)
|
||
iDrivigFlags |= moveStartHornDone; // nie trąbić aż do ruszenia
|
||
iDrivigFlags &= ~moveStartHornNow; // trąbienie zostało zorganizowane
|
||
}
|
||
}
|
||
|
||
// main ai update routine
|
||
auto const reactiontime = std::min( ReactionTime, 2.0 );
|
||
if( LastReactionTime < reactiontime ) { return; }
|
||
|
||
if (AIControllFlag) {
|
||
CheckTimeControllers();
|
||
}
|
||
|
||
LastReactionTime -= reactiontime;
|
||
// 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).
|
||
// 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
|
||
|
||
// przybliżona droga hamowania
|
||
// NOTE: we're ensuring some minimal braking distance to reduce ai flipping states between starting and braking
|
||
auto const velceil { std::max( 2.0, std::ceil( mvOccupied->Vel ) ) };
|
||
fBrakeDist = fDriverBraking * velceil * ( 40.0 + velceil );
|
||
if( fMass > 1000000.0 ) {
|
||
// korekta dla ciężkich, bo przeżynają - da to coś?
|
||
fBrakeDist *= 2.0;
|
||
}
|
||
if( ( -fAccThreshold > 0.05 )
|
||
&& ( mvOccupied->CategoryFlag == 1 ) ) {
|
||
fBrakeDist = velceil * velceil / 25.92 / -fAccThreshold;
|
||
}
|
||
if( mvOccupied->BrakeDelayFlag == bdelay_G ) {
|
||
// dla nastawienia G koniecznie należy wydłużyć drogę na czas reakcji
|
||
fBrakeDist += 2 * velceil;
|
||
}
|
||
if( ( mvOccupied->Vel > 15.0 )
|
||
&& ( mvControlling->EngineType == TEngineType::ElectricInductionMotor )
|
||
&& ( ( mvControlling->TrainType & dt_EZT ) != 0 ) ) {
|
||
// HACK: make the induction motor powered EMUs start braking slightly earlier
|
||
fBrakeDist += 10.0;
|
||
}
|
||
/*
|
||
// take into account effect of gravity (but to stay on safe side of calculations, only downhill)
|
||
if( fAccGravity > 0.025 ) {
|
||
fBrakeDist *= ( 1.0 + fAccGravity );
|
||
// TBD: use version which shortens route going uphill, too
|
||
//fBrakeDist = std::max( fBrakeDist, fBrakeDist * ( 1.0 + fAccGravity ) );
|
||
}
|
||
*/
|
||
// route scan
|
||
auto const routescanrange {
|
||
std::max(
|
||
750.0,
|
||
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( routescanrange ); // 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).
|
||
|
||
// check for potential collisions
|
||
{
|
||
// HACK: vehicle order in the consist is based on intended travel direction
|
||
// if our actual travel direction doesn't match that, we should be scanning from the other end of the consist
|
||
// we cast to int to avoid getting confused by microstutters
|
||
auto *frontvehicle { pVehicles[ ( static_cast<int>( mvOccupied->V ) * iDirection >= 0 ? end::front : end::rear ) ] };
|
||
|
||
int routescandirection;
|
||
// for moving vehicle determine heading from velocity; for standing fall back on the set direction
|
||
if( ( std::abs( frontvehicle->MoverParameters->V ) > 0.5 ? // ignore potential micro-stutters in oposite direction during "almost stop"
|
||
frontvehicle->MoverParameters->V > 0.0 :
|
||
( pVehicle->DirectionGet() == frontvehicle->DirectionGet() ?
|
||
iDirection > 0 :
|
||
iDirection < 0 ) ) ) {
|
||
// towards coupler 0
|
||
routescandirection = end::front;
|
||
}
|
||
else {
|
||
// towards coupler 1
|
||
routescandirection = end::rear;
|
||
}
|
||
/*
|
||
if( pVehicle->MoverParameters->ActiveCab < 0 ) {
|
||
// flip the scan direction in the rear cab
|
||
routescandirection ^= routescandirection;
|
||
}
|
||
*/
|
||
Obstacle = neighbour_data();
|
||
auto const lookup { frontvehicle->find_vehicle( routescandirection, routescanrange ) };
|
||
|
||
if( std::get<bool>( lookup ) == true ) {
|
||
|
||
Obstacle.vehicle = std::get<TDynamicObject *>( lookup );
|
||
Obstacle.vehicle_end = std::get<int>( lookup );
|
||
Obstacle.distance = std::get<double>( lookup );
|
||
|
||
if( Obstacle.distance < ( mvOccupied->CategoryFlag == 2 ? 50 : 100 ) ) {
|
||
// at short distances (re)calculate range between couplers directly
|
||
Obstacle.distance = TMoverParameters::CouplerDist( frontvehicle->MoverParameters, Obstacle.vehicle->MoverParameters );
|
||
}
|
||
}
|
||
}
|
||
// tu bedzie logika sterowania
|
||
if (AIControllFlag) {
|
||
|
||
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( ( /*mvOccupied->BrakeCtrlPos*/BrakeCtrlPosition == 0 )
|
||
&& ( AccDesired > 0.0 )
|
||
&& ( ( TestFlag( mvOccupied->SecuritySystem.Status, s_SHPebrake ) )
|
||
|| ( TestFlag( mvOccupied->SecuritySystem.Status, s_CAebrake ) ) ) ) {
|
||
//!!! hm, może po prostu normalnie sterować hamulcem?
|
||
//mvOccupied->BrakeLevelSet( 0 ); GBH
|
||
BrakeLevelSet(gbh_RP);
|
||
}
|
||
}
|
||
}
|
||
// basic emergency stop handling, while at it
|
||
if( ( true == mvOccupied->RadioStopFlag ) // radio-stop
|
||
&& ( mvOccupied->Vel == 0.0 ) // and actual stop
|
||
&& ( true == mvOccupied->Radio ) ) { // and we didn't touch the radio yet
|
||
// turning off the radio should reset the flag, during security system check
|
||
if( m_radiocontroltime > 5.0 ) {
|
||
// arbitrary delay between stop and disabling the radio
|
||
mvOccupied->Radio = false;
|
||
m_radiocontroltime = 0.0;
|
||
}
|
||
else {
|
||
m_radiocontroltime += reactiontime;
|
||
}
|
||
}
|
||
if( ( false == mvOccupied->Radio )
|
||
&& ( false == mvOccupied->RadioStopFlag ) ) {
|
||
// otherwise if it's safe to do so, turn the radio back on
|
||
if( m_radiocontroltime > 10.0 ) {
|
||
// arbitrary 5 sec delay before switching radio back on
|
||
mvOccupied->Radio = true;
|
||
m_radiocontroltime = 0.0;
|
||
}
|
||
else {
|
||
m_radiocontroltime += reactiontime;
|
||
}
|
||
}
|
||
}
|
||
|
||
switch (OrderCurrentGet())
|
||
{ // 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 = -1.0;
|
||
fMaxProximityDist = 0.0; //[m] dojechać maksymalnie
|
||
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)
|
||
{ // to robi tylko AI, wersję dla człowieka trzeba dopiero zrobić
|
||
// sprzęgi sprawdzamy w pierwszej kolejności, bo jak połączony, to koniec
|
||
auto *vehicle { pVehicles[ end::front ] };
|
||
auto *vehicleparameters { vehicle->MoverParameters };
|
||
int const end { ( vehicle->DirectionGet() > 0 ? end::front : end::rear ) };
|
||
auto const &neighbour { vehicleparameters->Neighbours[ end ] };
|
||
if( neighbour.vehicle != nullptr ) {
|
||
// próba podczepienia
|
||
vehicleparameters->Attach(
|
||
end, neighbour.vehicle_end,
|
||
neighbour.vehicle->MoverParameters,
|
||
iCoupler );
|
||
if( vehicleparameters->Couplers[ end ].CouplingFlag == iCoupler ) {
|
||
// 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
|
||
}
|
||
}
|
||
} // if (AIControllFlag) //koniec zblokowania, bo była zmienna lokalna
|
||
}
|
||
else {
|
||
// jak daleko, to jazda jak dla Shunt na kolizję
|
||
fMinProximityDist = 2.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
|
||
if( Obstacle.distance <= 20.0 ) {
|
||
// przy zderzeniu fTrackBlock nie jest miarodajne
|
||
// początek podczepiania, z wyłączeniem sprawdzania fTrackBlock
|
||
iDrivigFlags |= moveConnect;
|
||
}
|
||
}
|
||
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
|
||
break;
|
||
}
|
||
case Shunt: {
|
||
// na jaką odleglość i z jaką predkością ma podjechać
|
||
// TODO: test if we can use the distances calculation from obey_train
|
||
fMinProximityDist = std::min( 5 + iVehicles, 25 );
|
||
fMaxProximityDist = std::min( 10 + iVehicles, 50 );
|
||
// HACK: modern vehicles might brake slower at low speeds, increase safety margin as crude counter
|
||
if( mvControlling->EIMCtrlType > 0 ) {
|
||
fMinProximityDist += 5.0;
|
||
fMaxProximityDist += 5.0;
|
||
}
|
||
fVelPlus = 2.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania
|
||
// margines prędkości powodujący załączenie napędu
|
||
// były problemy z jazdą np. 3km/h podczas ładowania wagonów
|
||
fVelMinus = std::min( 0.1 * fShuntVelocity, 3.0 );
|
||
break;
|
||
}
|
||
case Loose_shunt: {
|
||
fMinProximityDist = -1.0;
|
||
fMaxProximityDist = 0.0; //[m] dojechać maksymalnie
|
||
fVelPlus = 2.0; // dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania
|
||
fVelMinus = 0.5; // margines prędkości powodujący załączenie napędu
|
||
break;
|
||
}
|
||
case Obey_train: {
|
||
// na jaka odleglosc i z jaka predkoscia ma podjechac do przeszkody
|
||
if( mvOccupied->CategoryFlag & 1 ) {
|
||
// jeśli pociąg
|
||
fMinProximityDist = clamp( 5 + iVehicles, 10, 15 );
|
||
fMaxProximityDist = clamp( 10 + iVehicles, 15, 40 );
|
||
|
||
if( IsCargoTrain ) {
|
||
// increase distances for cargo trains to take into account slower reaction to brakes
|
||
fMinProximityDist += 10.0;
|
||
fMaxProximityDist += 10.0;
|
||
/*
|
||
if( IsHeavyCargoTrain ) {
|
||
// cargo trains with high braking threshold may require even larger safety margin
|
||
fMaxProximityDist += 20.0;
|
||
}
|
||
*/
|
||
}
|
||
if( mvOccupied->Vel < 0.1 ) {
|
||
// jak stanie za daleko, to niech nie dociąga paru metrów
|
||
fMaxProximityDist = 50.0;
|
||
}
|
||
|
||
if( iDrivigFlags & moveLate ) {
|
||
// jeśli spóźniony, to gna
|
||
fVelMinus = 1.0;
|
||
// dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania
|
||
fVelPlus = 5.0;
|
||
}
|
||
else {
|
||
// gdy nie musi się sprężać
|
||
// margines prędkości powodujący załączenie napędu; min 1.0 żeby nie ruszał przy 0.1
|
||
fVelMinus = clamp( std::round( 0.05 * VelDesired ), 1.0, 5.0 );
|
||
// normalnie dopuszczalne przekroczenie to 5% prędkości ale nie więcej niż 5km/h
|
||
// bottom margin raised to 2 km/h to give the AI more leeway at low speed limits
|
||
fVelPlus = clamp( std::ceil( 0.05 * VelDesired ), 2.0, 5.0 );
|
||
}
|
||
}
|
||
else {
|
||
// samochod (sokista też)
|
||
fMinProximityDist = std::max( 3.5, mvOccupied->Vel * 0.2 );
|
||
fMaxProximityDist = std::max( 9.5, mvOccupied->Vel * 0.375 ); //[m]
|
||
// margines prędkości powodujący załączenie napędu
|
||
fVelMinus = 2.0;
|
||
// dopuszczalne przekroczenie prędkości na ograniczeniu bez hamowania
|
||
fVelPlus = std::min( 10.0, mvOccupied->Vel * 0.1 );
|
||
}
|
||
break;
|
||
}
|
||
case Bank: {
|
||
// TODO: implement
|
||
break;
|
||
}
|
||
default: {
|
||
fMinProximityDist = 5.0;
|
||
fMaxProximityDist = 10.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 OrderList[OrderPos]
|
||
|
||
switch (OrderCurrentGet())
|
||
{ // 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
|
||
case Shunt:
|
||
case Loose_shunt:
|
||
case Obey_train:
|
||
case Bank:
|
||
case Connect:
|
||
case Disconnect:
|
||
case Change_direction: // tryby wymagające jazdy
|
||
case Change_direction | Shunt: // zmiana kierunku podczas manewrów
|
||
case Change_direction | Loose_shunt:
|
||
case Change_direction | Connect: // zmiana kierunku podczas podłączania
|
||
if ((OrderCurrentGet() & ( Obey_train | Bank )) == 0) // spokojne manewry
|
||
{
|
||
VelSignal = min_speed( VelSignal, 40.0 ); // jeśli manewry, to ograniczamy prędkość
|
||
|
||
// NOTE: this section should be moved to disconnect or plain removed, but it seems to be (mis)used by some scenarios
|
||
// to keep vehicle idling without moving :|
|
||
if( ( true == AIControllFlag )
|
||
&& ( iVehicleCount >= 0 )
|
||
&& ( false == TestFlag( iDrivigFlags, movePress ) )
|
||
&& ( iCoupler == 0 )
|
||
// && ( mvOccupied->Vel > 0.0 )
|
||
&& ( pVehicle->MoverParameters->Couplers[ end::front ].Connected == nullptr )
|
||
&& ( pVehicle->MoverParameters->Couplers[ end::rear ].Connected == nullptr ) ) {
|
||
SetVelocity(0, 0, stopJoin); // 1. faza odczepiania: zatrzymanie
|
||
// WriteLog("Zatrzymanie w celu odczepienia");
|
||
AccPreferred = std::min( 0.0, AccPreferred );
|
||
}
|
||
|
||
}
|
||
else
|
||
SetDriverPsyche(); // Ra: było w PrepareEngine(), potrzebne tu?
|
||
|
||
if (OrderCurrentGet() & ( Shunt | Loose_shunt | Obey_train | Bank | 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( ( OrderCurrentGet() & ( Shunt | Loose_shunt | Obey_train | Bank ) )
|
||
&& ( iDrivigFlags & moveStopHere ) ) {
|
||
// zakaz ruszania z miejsca bez otrzymania wolnej drogi
|
||
WaitingTime = -WaitingExpireTime;
|
||
}
|
||
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 (Obstacle.distance > fDriverDist)
|
||
if (AIControllFlag)
|
||
{
|
||
PrepareEngine(); // zmieni ustawiony kierunek
|
||
SetVelocity(-1, -1); // jak się nastał, to niech jedzie
|
||
WaitingTime = 0.0;
|
||
}
|
||
else {
|
||
// użytkownikowi pozwalamy jechać (samochodem?)
|
||
SetVelocity( -1, -1 );
|
||
}
|
||
}
|
||
}
|
||
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( ( true == AIControllFlag)
|
||
&& ( true == TestFlag( OrderCurrentGet(), Change_direction ) ) ) {
|
||
// sprobuj zmienic kierunek (może być zmieszane z jeszcze jakąś komendą)
|
||
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( OrderCurrentGet() & ( Shunt | Loose_shunt | Connect ) ) {
|
||
// jeśli dalej mamy manewry
|
||
if( false == TestFlag( iDrivigFlags, moveStopHere ) ) {
|
||
// o ile nie ma stać w miejscu,
|
||
// jechać od razu w przeciwną stronę i nie trąbić z tego tytułu:
|
||
iDrivigFlags &= ~moveStartHorn;
|
||
SetVelocity( fShuntVelocity, fShuntVelocity );
|
||
}
|
||
}
|
||
}
|
||
} // Change_direction (tylko dla AI)
|
||
|
||
if( ( true == AIControllFlag )
|
||
&& ( iEngineActive == 0 )
|
||
&& ( OrderCurrentGet() & ( Change_direction | Connect | Disconnect | Shunt | Loose_shunt | Obey_train | Bank ) ) ) {
|
||
// jeśli coś ma robić to niech odpala do skutku
|
||
PrepareEngine();
|
||
}
|
||
|
||
// ustalanie zadanej predkosci
|
||
if (iDrivigFlags & moveActive) {
|
||
|
||
SetDriverPsyche(); // ustawia AccPreferred (potrzebne tu?)
|
||
|
||
// 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 )
|
||
&& ( TrainParams->TTVmax > 0.0 ) ) {
|
||
// jeśli ma rozkład i ograniczenie w rozkładzie to nie przekraczać rozkladowej
|
||
VelDesired = min_speed( VelDesired, TrainParams->TTVmax );
|
||
}
|
||
// szukanie optymalnych wartości
|
||
AccDesired = AccPreferred; // AccPreferred wynika z osobowości mechanika
|
||
VelNext = VelDesired; // maksymalna prędkość wynikająca z innych czynników niż trajektoria ruchu
|
||
ActualProximityDist = routescanrange; // funkcja Update() może pozostawić wartości bez zmian
|
||
// Ra: odczyt (ActualProximityDist), (VelNext) i (AccPreferred) z tabelki prędkosci
|
||
TCommandType comm = TableUpdate(VelDesired, ActualProximityDist, VelNext, AccDesired);
|
||
|
||
switch (comm) {
|
||
// ustawienie VelSignal - trochę proteza = do przemyślenia
|
||
case TCommandType::cm_Ready: // W4 zezwolił na jazdę
|
||
// ewentualne doskanowanie trasy za W4, który zezwolił na jazdę
|
||
TableCheck( routescanrange);
|
||
TableUpdate(VelDesired, ActualProximityDist, VelNext, AccDesired); // aktualizacja po skanowaniu
|
||
if (VelNext == 0.0)
|
||
break; // ale jak coś z przodu zamyka, to ma stać
|
||
if (iDrivigFlags & moveStopCloser)
|
||
VelSignal = -1.0; // ma czekać na sygnał z sygnalizatora!
|
||
case TCommandType::cm_SetVelocity: // od wersji 357 semafor nie budzi wyłączonej lokomotywy
|
||
if( ( OrderCurrentGet() & ~( Shunt | Loose_shunt | Obey_train | Bank ) ) == 0 ) // jedzie w dowolnym trybie albo Wait_for_orders
|
||
if (fabs(VelSignal) >= 1.0) // 0.1 nie wysyła się do samochodow, bo potem nie ruszą
|
||
PutCommand("SetVelocity", VelSignal, VelNext, nullptr); // komenda robi dodatkowe operacje
|
||
break;
|
||
case TCommandType::cm_ShuntVelocity: // od wersji 357 Tm nie budzi wyłączonej lokomotywy
|
||
if( ( OrderCurrentGet() & ~( Shunt | Loose_shunt | Obey_train | Bank ) ) == 0 ) // jedzie w dowolnym trybie albo Wait_for_orders
|
||
PutCommand("ShuntVelocity", VelSignal, VelNext, nullptr);
|
||
else if (iCoupler) // jeśli jedzie w celu połączenia
|
||
SetVelocity(VelSignal, VelNext);
|
||
break;
|
||
case TCommandType::cm_Command: // komenda z komórki
|
||
if( ( OrderCurrentGet() & ~( Shunt | Loose_shunt | Obey_train | Bank ) ) == 0 ) {
|
||
// jedzie w dowolnym trybie albo Wait_for_orders
|
||
if( mvOccupied->Vel < 0.1 ) {
|
||
// dopiero jak stanie
|
||
/*
|
||
PutCommand( eSignNext->text(), eSignNext->value( 1 ), eSignNext->value( 2 ), nullptr );
|
||
eSignNext->StopCommandSent(); // się wykonało już
|
||
*/ // replacement of the above
|
||
eSignNext->send_command( *this );
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
// disconnect mode potentially overrides scan results
|
||
// TBD: when in this mode skip scanning altogether?
|
||
if( ( true == AIControllFlag )
|
||
&& ( ( OrderCurrentGet() & Disconnect ) != 0 ) ) {
|
||
|
||
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( iVehicleCount >= 0 ) {
|
||
// zmieni się po odczepieniu
|
||
WriteLog( mvOccupied->Name + " dociskanie..." );
|
||
while( mvOccupied->DecLocalBrakeLevel( 1 ) ) { // dociśnij sklad
|
||
;
|
||
}
|
||
IncSpeed();
|
||
}
|
||
WriteLog(mvOccupied->Name + " odczepianie w kierunku " + std::to_string(mvOccupied->DirAbsolute));
|
||
TDynamicObject *p = pVehicle; // pojazd do odczepienia, w (pVehicle) siedzi AI
|
||
int d; // numer sprzęgu, który sprawdzamy albo odczepiamy
|
||
int n = iVehicleCount; // ile wagonów ma zostać
|
||
do
|
||
{ // szukanie pojazdu do odczepienia
|
||
d = p->DirectionGet() > 0 ?
|
||
end::front :
|
||
end::rear; // numer sprzęgu od strony czoła składu
|
||
// if (p->MoverParameters->Couplers[d].CouplerType==Articulated)
|
||
// //jeśli sprzęg typu wózek (za mało)
|
||
if (p->MoverParameters->Couplers[d].CouplingFlag & ctrain_depot) // jeżeli sprzęg zablokowany
|
||
// if (p->GetTrack()->) //a nie stoi na torze warsztatowym
|
||
// (ustalić po czym poznać taki tor)
|
||
++n; // to liczymy człony jako jeden
|
||
p->MoverParameters->BrakeReleaser(1); // wyluzuj pojazd, aby dało się dopychać
|
||
// GBH p->MoverParameters->BrakeLevelSet(0); // hamulec na zero, aby nie hamował
|
||
BrakeLevelSet(gbh_RP);
|
||
if (n)
|
||
{ // jeśli jeszcze nie koniec
|
||
p = p->Prev(); // kolejny w stronę czoła składu (licząc od tyłu), bo dociskamy
|
||
if (!p)
|
||
iVehicleCount = -2,
|
||
n = 0; // nie ma co dalej sprawdzać, doczepianie zakończone
|
||
}
|
||
} while (n--);
|
||
if( ( p == nullptr )
|
||
|| ( p->MoverParameters->Couplers[ d ].Connected == nullptr ) ) {
|
||
// no target, or already just virtual coupling
|
||
WriteLog( mvOccupied->Name + " didn't find anything to disconnect." );
|
||
iVehicleCount = -2; // odczepiono, co było do odczepienia
|
||
} else if ( p->Dettach(d) == coupling::faux ) {
|
||
// tylko jeśli odepnie
|
||
WriteLog( mvOccupied->Name + " odczepiony." );
|
||
iVehicleCount = -2;
|
||
} // a jak nie, to dociskać dalej
|
||
}
|
||
if ((mvOccupied->Vel < 0.01) && !(iDrivigFlags & movePress))
|
||
{ // 2. faza odczepiania: zmień kierunek na przeciwny i dociśnij
|
||
// za radą yB ustawiamy pozycję 3 kranu (ruszanie kranem w innych miejscach
|
||
// powino zostać wyłączone)
|
||
// WriteLog("Zahamowanie składu");
|
||
if( mvOccupied->BrakeSystem == TBrakeSystem::ElectroPneumatic ) {
|
||
mvOccupied->BrakeLevelSet( mvOccupied->Handle->GetPos( bh_EPB ) );
|
||
}
|
||
else {
|
||
BrakeCtrlPosition = 3;
|
||
}
|
||
double p = mvOccupied->BrakePressureActual.PipePressureVal;
|
||
if( p < 3.9 ) {
|
||
// tu może być 0 albo -1 nawet
|
||
// TODO: zabezpieczenie przed dziwnymi CHK do czasu wyjaśnienia sensu 0 oraz -1 w tym miejscu
|
||
p = 3.9;
|
||
}
|
||
if (mvOccupied->BrakeSystem == TBrakeSystem::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 == TBrakeSystem::ElectroPneumatic ) {
|
||
// wyłączenie EP, gdy wystarczy (może nie być potrzebne, bo na początku jest)
|
||
mvOccupied->BrakeLevelSet( 0 );
|
||
}
|
||
WriteLog("Luzowanie lokomotywy i zmiana kierunku");
|
||
mvOccupied->BrakeReleaser(1); // wyluzuj lokomotywę; a ST45?
|
||
mvOccupied->DecLocalBrakeLevel(LocalBrakePosNo); // 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
|
||
}
|
||
}
|
||
else {
|
||
if( mvOccupied->Vel > 0.01 ) {
|
||
// 1st phase(?)
|
||
// bring it to stop if it's not already stopped
|
||
SetVelocity( 0, 0, stopJoin ); // wyłączyć przyspieszanie
|
||
}
|
||
}
|
||
} // 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
|
||
ZeroSpeed();
|
||
// ponowna zmiana kierunku
|
||
WriteLog( mvOccupied->Name + " 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
|
||
mvOccupied->BrakeReleaser(0); // wyłączyć luzowanie
|
||
}
|
||
}
|
||
|
||
// when loose shunting try to detect situations where engaged brakes in a consist to be pushed prevent movement
|
||
if( ( true == AIControllFlag )
|
||
&& ( ( OrderCurrentGet() & Loose_shunt ) != 0 )
|
||
&& ( Obstacle.distance < 1.0 )
|
||
&& ( AccDesired > 0.1 )
|
||
&& ( mvOccupied->Vel < 1.0 ) ){
|
||
|
||
auto *vehicle { Obstacle.vehicle };
|
||
auto const direction { ( vehicle->Prev() != nullptr ? end::front : end::rear ) };
|
||
while( vehicle != nullptr ) {
|
||
if( vehicle->MoverParameters->BrakePress > 0.2 ) {
|
||
vehicle->MoverParameters->BrakeLevelSet( 0 ); // hamulec na zero, aby nie hamował
|
||
vehicle->MoverParameters->BrakeReleaser( 1 ); // wyluzuj pojazd, aby dało się dopychać
|
||
}
|
||
// NOTE: we trust the consist to be arranged in a valid chain
|
||
// TBD, TODO: traversal direction validation?
|
||
vehicle = ( direction == end::front ? vehicle->Prev() : vehicle->Next() );
|
||
}
|
||
}
|
||
|
||
if( true == TestFlag( OrderCurrentGet(), Change_direction ) ) {
|
||
// if ordered to change direction, try to stop
|
||
SetVelocity( 0, 0, stopDir );
|
||
}
|
||
|
||
if( VelNext == 0.0 ) {
|
||
if( !( OrderCurrentGet() & ~( Shunt | Loose_shunt | Connect ) ) ) {
|
||
// jedzie w Shunt albo Connect, albo Wait_for_orders
|
||
// 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( ( ( OrderCurrentGet() & Connect ) == 0 )
|
||
|| ( Obstacle.distance > std::min( 2000.0, FirstSemaphorDist ) ) ) {
|
||
|
||
if( ( comm = BackwardScan() ) != TCommandType::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 == TCommandType::cm_Command ) {
|
||
// jeśli komenda Shunt to ją odbierz bez przemieszczania się (np. odczep wagony po dopchnięciu do końca toru)
|
||
iDrivigFlags |= moveStopHere;
|
||
}
|
||
iDirectionOrder = -iDirection; // zmiana kierunku jazdy
|
||
// zmiana kierunku bez psucia kolejnych komend
|
||
OrderList[ OrderPos ] = TOrders( OrderCurrentGet() | Change_direction );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
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ść
|
||
// Ra: jazda na widoczność
|
||
if( Obstacle.distance < 5000 ) {
|
||
// mamy coś z przodu
|
||
// prędkość pojazdu z przodu (zakładając, że jedzie w tę samą stronę!!!)
|
||
auto const k { Obstacle.vehicle->MoverParameters->Vel };
|
||
if( k - vel < 5 ) {
|
||
// porównanie modułów prędkości [km/h]
|
||
// zatroszczyć się trzeba, jeśli tamten nie jedzie znacząco szybciej
|
||
ActualProximityDist = std::min<double>(
|
||
ActualProximityDist,
|
||
Obstacle.distance );
|
||
|
||
if( ActualProximityDist <= (
|
||
( mvOccupied->CategoryFlag & 2 ) ?
|
||
100.0 : // cars
|
||
250.0 ) ) { // others
|
||
// regardless of driving mode at close distance take precaution measures:
|
||
// match the other vehicle's speed or slow down if the other vehicle is stopped
|
||
VelDesired =
|
||
min_speed(
|
||
VelDesired,
|
||
std::max(
|
||
k,
|
||
( mvOccupied->CategoryFlag & 2 ) ?
|
||
40.0 : // cars
|
||
20.0 ) ); // others
|
||
if( vel > VelDesired + fVelPlus ) {
|
||
// if going too fast force some prompt braking
|
||
AccPreferred = std::min(
|
||
( ( mvOccupied->CategoryFlag & 2 ) ?
|
||
-0.65 : // cars
|
||
-0.30 ), // others
|
||
AccPreferred );
|
||
}
|
||
}
|
||
|
||
double const distance = Obstacle.distance - fMaxProximityDist - ( fBrakeDist * 1.15 ); // odległość bezpieczna zależy od prędkości
|
||
if( distance < 0.0 ) {
|
||
// jeśli odległość jest zbyt mała
|
||
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 | Loose_shunt ) ) != 0 ) {
|
||
// jeśli spinanie, to jechać dalej
|
||
AccPreferred = std::min( 0.35, AccPreferred ); // nie hamuj
|
||
VelDesired = std::floor(
|
||
min_speed(
|
||
VelDesired,
|
||
( Obstacle.distance > 150 ?
|
||
k + 20.0:
|
||
std::min( 8.0, k + 4.0 ) ) ) );
|
||
VelNext = std::floor( std::min( 8.0, k + 2.0 ) ); // i pakuj się na tamtego
|
||
}
|
||
else {
|
||
// a normalnie to hamować
|
||
VelNext = 0.0;
|
||
if( Obstacle.distance <= fMinProximityDist ) {
|
||
VelDesired = 0.0;
|
||
}
|
||
|
||
if( ( mvOccupied->CategoryFlag & 1 )
|
||
&& ( OrderCurrentGet() & Obey_train ) ) {
|
||
// trains which move normally should try to stop at safe margin
|
||
ActualProximityDist -= fDriverDist;
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
// jeśli oba jadą, to przyhamuj lekko i ogranicz prędkość
|
||
if( Obstacle.distance < (
|
||
( mvOccupied->CategoryFlag & 2 ) ?
|
||
fMaxProximityDist + 0.5 * vel : // cars
|
||
2.0 * fMaxProximityDist + 2.0 * vel ) ) { //others
|
||
// jak tamten jedzie wolniej a jest w drodze hamowania
|
||
AccPreferred = std::min( -0.9, AccPreferred );
|
||
VelNext = min_speed( std::round( k ) - 5.0, VelDesired );
|
||
if( Obstacle.distance <= (
|
||
( mvOccupied->CategoryFlag & 2 ) ?
|
||
fMaxProximityDist : // cars
|
||
2.0 * fMaxProximityDist ) ) { //others
|
||
// try to force speed change if obstacle is really close
|
||
VelDesired = VelNext;
|
||
}
|
||
}
|
||
}
|
||
ReactionTime = (
|
||
mvOccupied->Vel > 0.01 ?
|
||
0.1 : // orientuj się, bo jest goraco
|
||
2.0 ); // we're already standing still, so take it easy
|
||
}
|
||
else {
|
||
if( OrderCurrentGet() & Connect ) {
|
||
// if there's something nearby in the connect mode don't speed up too much
|
||
VelDesired =
|
||
min_speed(
|
||
VelDesired,
|
||
( Obstacle.distance > 100 ?
|
||
20.0 :
|
||
4.0 ) );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// sprawdzamy możliwe ograniczenia prędkości
|
||
if( VelSignal >= 0 ) {
|
||
// jeśli skład był zatrzymany na początku i teraz już może jechać
|
||
VelDesired =
|
||
min_speed(
|
||
VelDesired,
|
||
VelSignal );
|
||
}
|
||
if( mvOccupied->RunningTrack.Velmax >= 0 ) {
|
||
// ograniczenie prędkości z trajektorii ruchu
|
||
VelDesired =
|
||
min_speed(
|
||
VelDesired,
|
||
mvOccupied->RunningTrack.Velmax ); // uwaga na ograniczenia szlakowej!
|
||
}
|
||
if( VelforDriver >= 0 ) {
|
||
// tu jest zero przy zmianie kierunku jazdy
|
||
// Ra: tu może być 40, jeśli mechanik nie ma znajomości szlaaku, albo kierowca jeździ 70
|
||
VelDesired =
|
||
min_speed(
|
||
VelDesired,
|
||
VelforDriver );
|
||
}
|
||
if( fStopTime < 0 ) {
|
||
// czas postoju przed dalszą jazdą (np. na przystanku)
|
||
VelDesired = 0.0; // jak ma czekać, to nie ma jazdy
|
||
// verify progress of load exchange
|
||
auto exchangetime { 0.f };
|
||
auto *vehicle { pVehicles[ 0 ] };
|
||
while( vehicle != nullptr ) {
|
||
exchangetime = std::max( exchangetime, vehicle->LoadExchangeTime() );
|
||
vehicle = vehicle->Next();
|
||
}
|
||
if( exchangetime > 0 ) {
|
||
WaitingSet( exchangetime );
|
||
}
|
||
}
|
||
|
||
if( ( OrderCurrentGet() & Obey_train ) != 0 ) {
|
||
|
||
if( ( TrainParams->CheckTrainLatency() < 5.0 )
|
||
&& ( TrainParams->TTVmax > 0.0 ) ) {
|
||
// jesli nie spozniony to nie przekraczać rozkladowej
|
||
VelDesired =
|
||
min_speed(
|
||
VelDesired,
|
||
TrainParams->TTVmax );
|
||
}
|
||
}
|
||
|
||
if( ( OrderCurrentGet() & ( Shunt | Loose_shunt | Obey_train | Bank ) ) != 0 ) {
|
||
// w Connect nie, bo moveStopHere odnosi się do stanu po połączeniu
|
||
if( ( ( iDrivigFlags & moveStopHere ) != 0 )
|
||
&& ( vel < 0.01 )
|
||
&& ( VelSignalNext == 0.0 ) ) {
|
||
// jeśli ma czekać na wolną drogę, stoi a wyjazdu nie ma, to ma stać
|
||
VelDesired = 0.0;
|
||
}
|
||
}
|
||
|
||
if( OrderCurrentGet() == Wait_for_orders ) {
|
||
// wait means sit and wait
|
||
VelDesired = 0.0;
|
||
}
|
||
// end of speed caps checks
|
||
|
||
if( ( ( OrderCurrentGet() & Obey_train ) != 0 )
|
||
&& ( ( iDrivigFlags & moveGuardSignal ) != 0 )
|
||
&& ( VelDesired > 0.0 ) ) {
|
||
// komunikat od kierownika tu, bo musi być wolna droga i odczekany czas stania
|
||
iDrivigFlags &= ~moveGuardSignal; // tylko raz nadać
|
||
if( false == tsGuardSignal.empty() ) {
|
||
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 == 0 ) {
|
||
// jeśli nie przez radio
|
||
tsGuardSignal.owner( pVehicle );
|
||
// place virtual conductor some distance away
|
||
tsGuardSignal.offset( { pVehicle->MoverParameters->Dim.W * -0.75f, 1.7f, std::min( -20.0, -0.2 * fLength ) } );
|
||
tsGuardSignal.play( sound_flags::exclusive );
|
||
}
|
||
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
|
||
// TODO: proper system for sending/receiving radio messages
|
||
// place the sound in appropriate cab of the manned vehicle
|
||
tsGuardSignal.owner( pVehicle );
|
||
tsGuardSignal.offset( { 0.f, 2.f, pVehicle->MoverParameters->Dim.L * 0.4f * ( pVehicle->MoverParameters->ActiveCab < 0 ? -1 : 1 ) } );
|
||
tsGuardSignal.play( sound_flags::exclusive );
|
||
}
|
||
}
|
||
}
|
||
|
||
if( mvOccupied->Vel < 0.01 ) {
|
||
// Ra 2014-03: jesli skład stoi, to działa na niego składowa styczna grawitacji
|
||
AbsAccS = fAccGravity;
|
||
}
|
||
else {
|
||
AbsAccS = 0;
|
||
TDynamicObject *d = pVehicles[0]; // pojazd na czele składu
|
||
while (d)
|
||
{
|
||
AbsAccS += d->MoverParameters->TotalMass * d->MoverParameters->AccS * ( d->DirectionGet() == iDirection ? 1 : -1 );
|
||
d = d->Next(); // kolejny pojazd, podłączony od tyłu (licząc od czoła)
|
||
}
|
||
AbsAccS *= iDirection;
|
||
AbsAccS /= fMass;
|
||
}
|
||
AbsAccS_pub = AbsAccS;
|
||
|
||
#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 <= routescanrange )
|
||
&& ( 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 + 0.1 * vel ) ) ) {
|
||
// 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 dojedz do semafora/przeszkody
|
||
AccDesired = AccPreferred;
|
||
}
|
||
}
|
||
else if (ActualProximityDist > fMinProximityDist) {
|
||
// jedzie szybciej, niż trzeba na końcu ActualProximityDist, ale jeszcze jest daleko
|
||
if (ActualProximityDist < fMaxProximityDist) {
|
||
// jak minął już maksymalny dystans po prostu hamuj (niski stopień)
|
||
// ma stanąć, a jest w drodze hamowania albo ma jechać
|
||
/*
|
||
VelDesired = min_speed( VelDesired, VelNext );
|
||
*/
|
||
if( VelNext == 0.0 ) {
|
||
if( mvOccupied->CategoryFlag & 1 ) {
|
||
// trains
|
||
if( ( OrderCurrentGet() & ( Shunt | Connect ) )
|
||
&& ( Obstacle.distance < 50 ) ) {
|
||
// crude detection of edge case, if approaching another vehicle coast slowly until min distance
|
||
// this should allow to bunch up trainsets more on sidings
|
||
VelDesired = min_speed( 5.0, VelDesired );
|
||
}
|
||
else {
|
||
// hamowanie tak, aby stanąć
|
||
VelDesired = VelNext;
|
||
AccDesired = ( VelNext * VelNext - vel * vel ) / ( 25.92 * ( ActualProximityDist + 0.1 - 0.5*fMinProximityDist ) );
|
||
AccDesired = std::min( AccDesired, fAccThreshold );
|
||
}
|
||
}
|
||
else {
|
||
// for cars (and others) coast at low speed until we hit min proximity range
|
||
VelDesired = min_speed( VelDesired, 5.0 );
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
// outside of max safe range
|
||
AccDesired = AccPreferred;
|
||
if( vel > min_speed( (ActualProximityDist > 10.0 ? 10.0 : 5.0 ), VelDesired ) ) {
|
||
// allow to coast at reasonably low speed
|
||
auto const brakingdistance { fBrakeDist * braking_distance_multiplier( VelNext ) };
|
||
auto const slowdowndistance { (
|
||
mvOccupied->CategoryFlag == 2 ? // cars can stop on a dime, for bigger vehicles we enforce some minimal braking distance
|
||
brakingdistance :
|
||
std::max(
|
||
( ( OrderCurrentGet() & Connect ) == 0 ? 100.0 : 25.0 ),
|
||
brakingdistance ) ) };
|
||
if( ( brakingdistance + std::max( slowdowndistance, fMaxProximityDist ) ) >= ( ActualProximityDist - fMaxProximityDist ) ) {
|
||
// don't slow down prematurely; as long as we have room to come to a full stop at a safe distance, we're good
|
||
// ensure some minimal coasting speed, otherwise a vehicle entering this zone at very low speed will be crawling forever
|
||
auto const brakingpointoffset = VelNext * braking_distance_multiplier( VelNext );
|
||
AccDesired = std::min(
|
||
AccDesired,
|
||
( VelNext * VelNext - vel * vel )
|
||
/ ( 25.92
|
||
* std::max(
|
||
ActualProximityDist - brakingpointoffset,
|
||
std::min(
|
||
ActualProximityDist,
|
||
brakingpointoffset ) )
|
||
+ 0.1 ) ); // najpierw hamuje mocniej, potem zluzuje
|
||
}
|
||
}
|
||
}
|
||
AccDesired = std::min( AccDesired, AccPreferred );
|
||
}
|
||
else {
|
||
// jest bliżej niż fMinProximityDist
|
||
// utrzymuj predkosc bo juz blisko
|
||
/*
|
||
VelDesired = min_speed( VelDesired, VelNext );
|
||
*/
|
||
if( VelNext == 0.0 ) {
|
||
VelDesired = VelNext;
|
||
}
|
||
else {
|
||
if( vel <= VelNext + fVelPlus ) {
|
||
// jeśli niewielkie przekroczenie, ale ma jechać
|
||
AccDesired = std::max( 0.0, AccPreferred ); // to olej (zacznij luzować)
|
||
}
|
||
}
|
||
ReactionTime = 0.1; // i orientuj się szybciej
|
||
}
|
||
}
|
||
else {
|
||
// zatrzymany (vel==0.0)
|
||
if( VelNext > 0.0 ) {
|
||
// można jechać
|
||
AccDesired = AccPreferred;
|
||
}
|
||
else {
|
||
// jeśli daleko jechać nie można
|
||
if( ActualProximityDist > (
|
||
( mvOccupied->CategoryFlag & 2 ) ?
|
||
fMinProximityDist : // cars
|
||
fMaxProximityDist ) ) { // trains and others
|
||
// ale ma kawałek do sygnalizatora
|
||
if( AccPreferred > 0.0 ) {
|
||
// dociagnij do semafora;
|
||
AccDesired = AccPreferred;
|
||
}
|
||
else {
|
||
//stoj
|
||
VelDesired = 0.0;
|
||
}
|
||
}
|
||
else {
|
||
// VelNext=0 i stoi bliżej niż fMaxProximityDist
|
||
VelDesired = 0.0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
// gdy jedzie wolniej niż potrzeba, albo nie ma przeszkód na drodze
|
||
// normalna jazda
|
||
AccDesired = (
|
||
VelDesired != 0.0 ?
|
||
AccPreferred :
|
||
-0.01 );
|
||
}
|
||
// koniec predkosci nastepnej
|
||
|
||
// decisions based on current speed
|
||
if( mvOccupied->CategoryFlag == 1 ) {
|
||
|
||
// on flats or uphill we can be less careful
|
||
if( vel > VelDesired ) {
|
||
// jesli jedzie za szybko do AKTUALNEGO
|
||
if( VelDesired == 0.0 ) {
|
||
// jesli stoj, to hamuj, ale i tak juz za pozno :)
|
||
AccDesired = std::min( AccDesired, -0.85 ); // hamuj solidnie
|
||
}
|
||
else {
|
||
// slow down, not full stop
|
||
if( vel > ( VelDesired + fVelPlus ) ) {
|
||
// hamuj tak średnio
|
||
AccDesired = std::min( AccDesired, -0.25 );
|
||
}
|
||
else {
|
||
// o 5 km/h to olej (zacznij luzować)
|
||
AccDesired = std::min(
|
||
AccDesired, // but don't override decceleration for VelNext
|
||
std::max( 0.0, AccPreferred ) );
|
||
}
|
||
}
|
||
}
|
||
|
||
if( fAccGravity > 0.025 ) {
|
||
// going sharply downhill we may need to start braking sooner than usual
|
||
// try to estimate increase of current velocity before engaged brakes start working
|
||
auto const speedestimate = vel + ( 1.0 - fBrake_a0[ 0 ] ) * 30.0 * AbsAccS;
|
||
if( speedestimate > VelDesired ) {
|
||
// jesli jedzie za szybko do AKTUALNEGO
|
||
if( VelDesired == 0.0 ) {
|
||
// jesli stoj, to hamuj, ale i tak juz za pozno :)
|
||
AccDesired = std::min( AccDesired, -0.85 ); // hamuj solidnie
|
||
}
|
||
else {
|
||
// if it looks like we'll exceed maximum speed start thinking about slight slowing down
|
||
AccDesired = std::min( AccDesired, -0.25 );
|
||
// HACK: for cargo trains with high braking threshold ensure we cross that threshold
|
||
if( ( true == IsCargoTrain )
|
||
&& ( fBrake_a0[ 0 ] > 0.2 ) ) {
|
||
AccDesired -= clamp( fBrake_a0[ 0 ] - 0.2, 0.0, 0.15 );
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
// stop accelerating when close enough to target speed
|
||
AccDesired = std::min(
|
||
AccDesired, // but don't override decceleration for VelNext
|
||
interpolate( // ease off as you close to the target velocity
|
||
-0.06, AccPreferred,
|
||
clamp( VelDesired - speedestimate, 0.0, fVelMinus ) / fVelMinus ) );
|
||
}
|
||
// final tweaks
|
||
if( vel > 0.1 ) {
|
||
// going downhill also take into account impact of gravity
|
||
AccDesired -= fAccGravity;
|
||
// HACK: if the max allowed speed was exceeded something went wrong; brake harder
|
||
AccDesired -= 0.15 * clamp( vel - VelDesired, 0.0, 5.0 );
|
||
/*
|
||
if( ( vel > VelDesired )
|
||
&& ( ( mvOccupied->BrakeDelayFlag & bdelay_G ) != 0 )
|
||
&& ( fBrake_a0[ 0 ] > 0.2 ) ) {
|
||
AccDesired = clamp(
|
||
AccDesired - clamp( fBrake_a0[ 0 ] - 0.2, 0.0, 0.15 ),
|
||
-0.9, 0.9 );
|
||
}
|
||
*/
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
// for cars the older version works better
|
||
if( vel > VelDesired ) {
|
||
// jesli jedzie za szybko do AKTUALNEGO
|
||
if( VelDesired == 0.0 ) {
|
||
// jesli stoj, to hamuj, ale i tak juz za pozno :)
|
||
AccDesired = std::min( AccDesired, -0.9 ); // hamuj solidnie
|
||
}
|
||
else {
|
||
// slow down, not full stop
|
||
if( vel > ( VelDesired + fVelPlus ) ) {
|
||
// hamuj tak średnio
|
||
AccDesired = std::min( AccDesired, -fBrake_a0[ 0 ] * 0.5 );
|
||
}
|
||
else {
|
||
// o 5 km/h to olej (zacznij luzować)
|
||
AccDesired = std::min(
|
||
AccDesired, // but don't override decceleration for VelNext
|
||
std::max( 0.0, AccPreferred ) );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// koniec predkosci aktualnej
|
||
|
||
// last step sanity check, until the whole calculation is straightened out
|
||
AccDesired = std::min( AccDesired, AccPreferred );
|
||
AccDesired = clamp(
|
||
AccDesired,
|
||
( mvControlling->CategoryFlag == 2 ? -2.0 : -0.9 ),
|
||
( mvControlling->CategoryFlag == 2 ? 2.0 : 0.9 ) );
|
||
|
||
|
||
if ((-AccDesired > fBrake_a0[0] + 8 * fBrake_a1[0]) && (HelperState == 0))
|
||
{
|
||
HelperState = 1;
|
||
}
|
||
if ((-AccDesired > fBrake_a0[0] + 12 * fBrake_a1[0]) && (HelperState == 1))
|
||
{
|
||
HelperState = 2;
|
||
}
|
||
if ((-AccDesired > 0) && (HelperState == 2) && (-ActualProximityDist > 5))
|
||
{
|
||
HelperState = 3;
|
||
}
|
||
if ((-AccDesired < fBrake_a0[0] + 2 * fBrake_a1[0]) && (HelperState > 0) && (vel>1))
|
||
{
|
||
HelperState = 0;
|
||
}
|
||
|
||
if (AIControllFlag) {
|
||
// część wykonawcza tylko dla AI, dla człowieka jedynie napisy
|
||
|
||
// zapobieganie poslizgowi u nas
|
||
if (mvControlling->SlippingWheels && mvControlling->EngineType != TEngineType::ElectricInductionMotor) {
|
||
|
||
if( false == mvControlling->DecScndCtrl( 2 ) ) {
|
||
// bocznik na zero
|
||
mvControlling->DecMainCtrl( 1 );
|
||
}
|
||
DecBrake(); // cofnij hamulec
|
||
mvControlling->AntiSlippingButton();
|
||
++iDriverFailCount;
|
||
}
|
||
if (iDriverFailCount > maxdriverfails) {
|
||
|
||
Psyche = Easyman;
|
||
if (iDriverFailCount > maxdriverfails * 2)
|
||
SetDriverPsyche();
|
||
}
|
||
|
||
if( ( true == mvOccupied->RadioStopFlag ) // radio-stop
|
||
&& ( mvOccupied->Vel > 0.0 ) ) { // and still moving
|
||
// if the radio-stop was issued don't waste effort trying to fight it
|
||
ZeroSpeed(); // just throttle down...
|
||
return; // ...and don't touch any other controls
|
||
}
|
||
|
||
if( ( true == mvControlling->ConvOvldFlag ) // wywalił bezpiecznik nadmiarowy przetwornicy
|
||
|| ( false == IsLineBreakerClosed ) ) { // WS może wywalić z powodu błędu w drutach
|
||
// próba ponownego załączenia
|
||
PrepareEngine();
|
||
}
|
||
// włączanie bezpiecznika
|
||
if ((mvControlling->EngineType == TEngineType::ElectricSeriesMotor) ||
|
||
(mvControlling->TrainType & dt_EZT) ||
|
||
(mvControlling->EngineType == TEngineType::DieselElectric))
|
||
if (mvControlling->FuseFlag || Need_TryAgain)
|
||
{
|
||
Need_TryAgain = false; // true, jeśli druga pozycja w elektryku nie załapała
|
||
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()) {
|
||
++iDriverFailCount;
|
||
if (iDriverFailCount > maxdriverfails)
|
||
Psyche = Easyman;
|
||
if (iDriverFailCount > maxdriverfails * 2)
|
||
SetDriverPsyche();
|
||
}
|
||
}
|
||
// NOTE: as a stop-gap measure the routine is limited to trains only while car calculations seem off
|
||
if( mvControlling->CategoryFlag == 1 ) {
|
||
if( vel < VelDesired ) {
|
||
// don't adjust acceleration when going above current goal speed
|
||
if( -AccDesired * BrakeAccFactor() < (
|
||
( ( fReady > 0.4 )
|
||
|| ( VelNext > vel - 40.0 ) ) ?
|
||
fBrake_a0[ 0 ] * 0.8 :
|
||
-fAccThreshold )
|
||
/ ( 1.2 * braking_distance_multiplier( VelNext ) ) ) {
|
||
AccDesired = std::max( -0.06, AccDesired );
|
||
}
|
||
}
|
||
if( AccDesired < -0.1 ) {
|
||
// i orientuj się szybciej, jeśli hamujesz
|
||
ReactionTime = 0.25;
|
||
}
|
||
}
|
||
if (mvOccupied->BrakeSystem == TBrakeSystem::Pneumatic) {
|
||
// napełnianie uderzeniowe
|
||
if( ( mvOccupied->BrakeHandle == TBrakeHandle::FV4a )
|
||
|| ( mvOccupied->BrakeHandle == TBrakeHandle::MHZ_6P )
|
||
|| ( mvOccupied->BrakeHandle == TBrakeHandle::M394 ) ) {
|
||
|
||
if( /*GBH mvOccupied->BrakeCtrlPos*/BrakeCtrlPosition == -2 ) {
|
||
/*mvOccupied->*/BrakeLevelSet( gbh_RP );
|
||
}
|
||
|
||
// TODO: combine all releaser handling in single decision tree instead of having bits all over the place
|
||
if( ( AccDesired > -0.03 )
|
||
&& ( false == mvOccupied->Hamulec->Releaser() ) ) {
|
||
if( mvOccupied->PipePress < 3.0 ) {
|
||
mvOccupied->BrakeReleaser( 1 );
|
||
}
|
||
if( ( mvOccupied->BrakePress > 0.4 )
|
||
&& ( mvOccupied->Hamulec->GetCRP() > 4.9 ) ) {
|
||
// wyluzuj lokomotywę, to szybciej ruszymy
|
||
mvOccupied->BrakeReleaser( 1 );
|
||
}
|
||
}
|
||
if( ( mvOccupied->PipePress > 3.0 )
|
||
&& ( mvOccupied->Hamulec->Releaser() ) ) {
|
||
// don't overcharge train brake pipe
|
||
mvOccupied->BrakeReleaser( 0 );
|
||
}
|
||
|
||
if( ( /*GBH mvOccupied->BrakeCtrlPos*/BrakeCtrlPosition == 0 )
|
||
&& ( AbsAccS < 0.03 )
|
||
&& ( AccDesired > -0.03 )
|
||
&& ( VelDesired - mvOccupied->Vel > 2.0 ) ) {
|
||
|
||
if( ( mvOccupied->EqvtPipePress < 4.5 )
|
||
&& ( fReady > 0.35 )
|
||
&& ( BrakeChargingCooldown >= 0.0 )
|
||
&& ( ( ActualProximityDist > 100.0 ) // don't charge if we're about to be braking soon
|
||
|| ( min_speed( mvOccupied->Vel, VelNext ) == mvOccupied->Vel ) ) ) {
|
||
|
||
if( ( iDrivigFlags & moveOerlikons )
|
||
|| ( true == IsCargoTrain ) ) {
|
||
// napełnianie w Oerlikonie
|
||
/* mvOccupied->BrakeLevelSet( mvOccupied->Handle->GetPos( bh_FS ) ); GBH */
|
||
BrakeLevelSet( gbh_FS );
|
||
// don't charge the brakes too often, or we risk overcharging
|
||
BrakeChargingCooldown = -120.0;
|
||
}
|
||
}
|
||
/*
|
||
// NOTE: disabled, duplicate of release activation in #5732
|
||
else if( Need_BrakeRelease ) {
|
||
Need_BrakeRelease = false;
|
||
mvOccupied->BrakeReleaser( 1 );
|
||
}
|
||
*/
|
||
}
|
||
|
||
if( ( /*GBH mvOccupied->BrakeCtrlPos*/BrakeCtrlPosition < 0 )
|
||
&& ( mvOccupied->EqvtPipePress > ( fReady < 0.25 ? 5.1 : 5.2 ) ) ) {
|
||
/* GBH mvOccupied->BrakeLevelSet( mvOccupied->Handle->GetPos( bh_RP ) ); */
|
||
BrakeLevelSet( gbh_RP );
|
||
}
|
||
}
|
||
}
|
||
#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( ( vel < 10.0 )
|
||
&& ( AccDesired > 0.1 ) ) {
|
||
// Ra 2F1H: jeśli prędkość jest mała, a można przyspieszać,
|
||
// to nie ograniczać przyspieszenia do 0.5m/ss
|
||
// przy małych prędkościach może być trudno utrzymać
|
||
AccDesired = std::max( 0.9, AccDesired );
|
||
}
|
||
// małe przyspieszenie
|
||
// Ra 2F1I: wyłączyć kiedyś to uśrednianie i przeanalizować skanowanie, czemu migocze
|
||
if (AccDesired > -0.05) // hamowania lepeiej nie uśredniać
|
||
AccDesired = fAccDesiredAv =
|
||
0.2 * AccDesired +
|
||
0.8 * fAccDesiredAv; // uśrednione, żeby ograniczyć migotanie
|
||
if( VelDesired == 0.0 ) {
|
||
// Ra 2F1J: jeszcze jedna prowizoryczna łatka
|
||
if( AccDesired >= -0.01 ) {
|
||
AccDesired = -0.01;
|
||
}
|
||
}
|
||
if( AccDesired >= 0.0 ) {
|
||
if( true == TestFlag( iDrivigFlags, movePress ) ) {
|
||
// wyluzuj lokomotywę - może być więcej!
|
||
mvOccupied->BrakeReleaser( 1 );
|
||
}
|
||
else if( OrderCurrentGet() != Disconnect ) {
|
||
// przy odłączaniu nie zwalniamy tu hamulca
|
||
if( ( fAccGravity * fAccGravity < 0.001 ?
|
||
true :
|
||
AccDesired > 0.0 ) ) {
|
||
// on slopes disengage the brakes only if you actually intend to accelerate
|
||
while( true == DecBrake() ) { ; } // jeśli przyspieszamy, to nie hamujemy
|
||
}
|
||
}
|
||
}
|
||
// 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( ( AccDesired > -0.06 ) // don't add power if not asked for actual speed-up
|
||
&& ( AccDesired - AbsAccS > 0.05 ) ) {
|
||
// jeśli przyspieszenie pojazdu jest mniejsze niż żądane oraz...
|
||
if( vel < (
|
||
VelDesired == 1.0 ? // work around for trains getting stuck on tracks with speed limit = 1
|
||
VelDesired :
|
||
VelDesired - fVelMinus ) ) {
|
||
// ...jeśli prędkość w kierunku czoła jest mniejsza od dozwolonej o margines
|
||
if( ( ActualProximityDist > (
|
||
( mvOccupied->CategoryFlag & 2 ) ?
|
||
fMinProximityDist : // cars are allowed to move within min proximity distance
|
||
fMaxProximityDist ) ? // other vehicle types keep wider margin
|
||
true :
|
||
( vel + 1.0 ) < VelNext ) ) {
|
||
// to można przyspieszyć
|
||
IncSpeed();
|
||
}
|
||
}
|
||
}
|
||
// yB: usunięte różne dziwne warunki, oddzielamy część zadającą od wykonawczej
|
||
// zmniejszanie predkosci
|
||
// margines dla prędkości jest doliczany tylko jeśli oczekiwana prędkość jest większa od 5km/h
|
||
if( false == TestFlag( iDrivigFlags, movePress ) ) {
|
||
// jeśli nie dociskanie
|
||
if( AccDesired < -0.05 ) {
|
||
ZeroSpeed();
|
||
}
|
||
else if( ( vel > VelDesired )
|
||
|| ( fAccGravity < -0.01 ?
|
||
AccDesired < 0.0 :
|
||
(AbsAccS > AccDesired && AccDesired < 0.75) ) ) {
|
||
// jak za bardzo przyspiesza albo prędkość przekroczona
|
||
// dodany wyjatek na "pelna w przod"
|
||
DecSpeed(); // pojedyncze cofnięcie pozycji, bo na zero to przesada
|
||
}
|
||
}
|
||
if( mvOccupied->TrainType == dt_EZT ) {
|
||
// właściwie, to warunek powinien być na działający EP
|
||
// Ra: to dobrze hamuje EP w EZT
|
||
// HACK: when going downhill be more responsive to desired deceleration
|
||
auto const accthreshold { (
|
||
fAccGravity < 0.025 ?
|
||
fAccThreshold :
|
||
std::max( -0.2, fAccThreshold ) ) };
|
||
if( ( AccDesired <= accthreshold ) // jeśli hamować - u góry ustawia się hamowanie na fAccThreshold
|
||
&& ( ( AbsAccS > AccDesired )
|
||
|| ( /*GBH mvOccupied->BrakeCtrlPos*/BrakeCtrlPosition < 0 ) ) ) {
|
||
// hamować bardziej, gdy aktualne opóźnienie hamowania mniejsze niż (AccDesired)
|
||
IncBrake();
|
||
}
|
||
else if( OrderCurrentGet() != 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
|
||
// TBD: check if the condition isn't redundant with the DecBrake() code
|
||
if( /*GBH mvOccupied->BrakeCtrlPos*/BrakeCtrlPosition >= 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 ) );
|
||
}
|
||
}
|
||
} // order != disconnect
|
||
} // type & dt_ezt
|
||
else {
|
||
// a stara wersja w miarę dobrze działa na składy wagonowe
|
||
if( ( ( fAccGravity < -0.05 ) && ( vel < -0.1 ) ) // brake if uphill and slipping back
|
||
|| ( ( AccDesired < fAccGravity - 0.1 ) && ( AbsAccS > AccDesired + fBrake_a1[ 0 ] ) ) ) {
|
||
// u góry ustawia się hamowanie na fAccThreshold
|
||
if( ( fBrakeTime < 0.0 )
|
||
|| ( AccDesired < fAccGravity - 0.5 )
|
||
|| ( /*GBH mvOccupied->BrakeCtrlPos*/BrakeCtrlPosition <= 0 ) ) {
|
||
// jeśli upłynął czas reakcji hamulca, chyba że nagłe albo luzował
|
||
if( true == IncBrake() ) {
|
||
fBrakeTime =
|
||
3.0
|
||
+ 0.5 * ( (
|
||
mvOccupied->BrakeDelayFlag > bdelay_G ?
|
||
mvOccupied->BrakeDelay[ 1 ] :
|
||
mvOccupied->BrakeDelay[ 3 ] )
|
||
- 3.0 );
|
||
// 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 )
|
||
&& ( ( AccDesired - fBrake_a1[0]*0.51 ) ) - AbsAccS > 0.05 ) {
|
||
// jak hamuje, to nie tykaj kranu za często
|
||
// yB: luzuje hamulec dopiero przy różnicy opóźnień rzędu 0.2
|
||
if( OrderCurrentGet() != Disconnect ) {
|
||
// przy odłączaniu nie zwalniamy tu hamulca
|
||
DecBrake(); // tutaj zmniejszało o 1 przy odczepianiu
|
||
}
|
||
fBrakeTime = (
|
||
mvOccupied->BrakeDelayFlag > bdelay_G ?
|
||
mvOccupied->BrakeDelay[ 0 ] :
|
||
mvOccupied->BrakeDelay[ 2 ] )
|
||
/ 3.0;
|
||
fBrakeTime *= 0.5; // Ra: tymczasowo, bo przeżyna S1
|
||
}
|
||
// stop-gap measure to ensure cars actually brake to stop even when above calculactions go awry
|
||
// instead of releasing the brakes and creeping into obstacle at 1-2 km/h
|
||
if( mvControlling->CategoryFlag == 2 ) {
|
||
if( ( VelDesired == 0.0 )
|
||
&& ( vel > VelDesired )
|
||
&& ( ActualProximityDist <= fMinProximityDist )
|
||
&& ( mvOccupied->LocalBrakePosA < 0.01 ) ) {
|
||
IncBrake();
|
||
}
|
||
}
|
||
}
|
||
// Mietek-end1
|
||
SpeedSet(); // ciągla regulacja prędkości
|
||
#if LOGVELOCITY
|
||
WriteLog("BrakePos=" + AnsiString(mvOccupied->BrakeCtrlPos) + ", MainCtrl=" +
|
||
AnsiString(mvControlling->MainCtrlPos));
|
||
#endif
|
||
} // if (AIControllFlag)
|
||
} // 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( ( ( OrderCurrentGet() & ( Disconnect | Connect ) ) == 0 )
|
||
&& ( std::abs( fAccGravity ) < 0.01 ) ) {
|
||
// przy (p)odłączaniu nie zwalniamy tu hamulca
|
||
// only do this on flats, on slopes keep applied the train brake
|
||
if( ( mvOccupied->Vel < 0.01 )
|
||
&& ( ( VelDesired == 0.0 )
|
||
|| ( AccDesired == 0.0 ) ) ) {
|
||
if( mvOccupied->BrakeCtrlPos == mvOccupied->Handle->GetPos( bh_RP ) ) {
|
||
// dodatkowy na pozycję 1
|
||
mvOccupied->IncLocalBrakeLevel( 1 );
|
||
}
|
||
else {
|
||
mvOccupied->BrakeLevelSet( mvOccupied->Handle->GetPos( bh_RP ) ); //GBH
|
||
BrakeLevelSet(gbh_RP);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
break; // rzeczy robione przy jezdzie
|
||
} // switch (OrderList[OrderPos])
|
||
if (AIControllFlag) SetTimeControllers();
|
||
}
|
||
|
||
// configures vehicle heating given current situation; returns: true if vehicle can be operated normally, false otherwise
|
||
bool
|
||
TController::UpdateHeating() {
|
||
|
||
switch( mvControlling->EngineType ) {
|
||
|
||
case TEngineType::DieselElectric:
|
||
case TEngineType::DieselEngine: {
|
||
|
||
auto const &heat { mvControlling->dizel_heat };
|
||
|
||
// determine whether there's need to enable the water heater
|
||
// if the heater has configured maximum temperature, it'll disable itself automatically, so we can leave it always running
|
||
// otherwise enable the heater only to maintain minimum required temperature
|
||
auto const lowtemperature { (
|
||
( ( heat.water.config.temp_min > 0 ) && ( heat.temperatura1 < heat.water.config.temp_min + ( mvControlling->WaterHeater.is_active ? 5 : 0 ) ) )
|
||
|| ( ( heat.water_aux.config.temp_min > 0 ) && ( heat.temperatura2 < heat.water_aux.config.temp_min + ( mvControlling->WaterHeater.is_active ? 5 : 0 ) ) )
|
||
|| ( ( heat.oil.config.temp_min > 0 ) && ( heat.To < heat.oil.config.temp_min + ( mvControlling->WaterHeater.is_active ? 5 : 0 ) ) ) ) };
|
||
auto const heateron { (
|
||
( mvControlling->WaterHeater.config.temp_max > 0 )
|
||
|| ( true == lowtemperature ) ) };
|
||
if( true == heateron ) {
|
||
// make sure the water pump is running before enabling the heater
|
||
if( false == mvControlling->WaterPump.is_active ) {
|
||
mvControlling->WaterPumpBreakerSwitch( true );
|
||
mvControlling->WaterPumpSwitch( true );
|
||
}
|
||
if( true == mvControlling->WaterPump.is_active ) {
|
||
mvControlling->WaterHeaterBreakerSwitch( true );
|
||
mvControlling->WaterHeaterSwitch( true );
|
||
mvControlling->WaterCircuitsLinkSwitch( true );
|
||
}
|
||
}
|
||
else {
|
||
// no need to heat anything up, switch the heater off
|
||
mvControlling->WaterCircuitsLinkSwitch( false );
|
||
mvControlling->WaterHeaterSwitch( false );
|
||
mvControlling->WaterHeaterBreakerSwitch( false );
|
||
// optionally turn off the water pump as well
|
||
if( mvControlling->WaterPump.start_type != start_t::battery ) {
|
||
mvControlling->WaterPumpSwitch( false );
|
||
mvControlling->WaterPumpBreakerSwitch( false );
|
||
}
|
||
}
|
||
|
||
return ( false == lowtemperature );
|
||
}
|
||
default: {
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
|
||
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( OrderCurrentGet() != Obey_train ) {
|
||
// reset light hints
|
||
m_lighthints[ end::front ] = m_lighthints[ end::rear ] = -1;
|
||
}
|
||
if( OrderCurrentGet() & ( Shunt | Loose_shunt | Connect | Obey_train | Bank ) ) {
|
||
CheckVehicles(); // sprawdzić światła
|
||
}
|
||
if( OrderCurrentGet() & ( Shunt | Loose_shunt | Connect ) ) {
|
||
// HACK: ensure consist doors will be closed on departure
|
||
iDrivigFlags |= moveDoorOpened;
|
||
}
|
||
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] != Wait_for_orders) && (OrderList[OrderTop] < Shunt)) // 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: [" + Order2Str( NewOrder ) + "]");
|
||
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((std::to_string(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
|
||
}
|
||
};
|
||
|
||
void TController::OrdersInit(double fVel)
|
||
{ // wypełnianie tabelki rozkazów na podstawie rozkładu
|
||
// ustawienie kolejności komend, niezależnie kto prowadzi
|
||
OrdersClear(); // usunięcie poprzedniej tabeli
|
||
OrderPush(Prepare_engine); // najpierw odpalenie silnika
|
||
if (TrainParams->TrainName == "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->DirectionChange() ? // 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(
|
||
t->StationName
|
||
+ " " + std::to_string(t->Ah) + ":" + std::to_string(t->Am)
|
||
+ ", " + std::to_string(t->Dh) + ":" + std::to_string(t->Dm)
|
||
+ " " + t->StationWare);
|
||
}
|
||
if (t->StationWare.find('@') != std::string::npos)
|
||
{ // 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) // 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 ) {
|
||
// jeśli nie ma prędkości początkowej, to śpi
|
||
SetVelocity( 0, 0, stopSleep );
|
||
}
|
||
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
|
||
// to do następnego W4 ma podjechać blisko
|
||
iDrivigFlags = ( iDrivigFlags & ~( moveStopHere ) ) | moveStopCloser;
|
||
}
|
||
else {
|
||
// czekać na sygnał
|
||
iDrivigFlags |= moveStopHere;
|
||
}
|
||
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
|
||
};
|
||
|
||
std::string TController::StopReasonText() const
|
||
{ // informacja tekstowa o przyczynie zatrzymania
|
||
if (eStopReason != 7) // zawalidroga będzie inaczej
|
||
return StopReasonTable[eStopReason];
|
||
else
|
||
return "Blocked by " + (pVehicles[0]->PrevAny()->name());
|
||
};
|
||
|
||
//----------------------------------------------------------------------------------------------------------------------
|
||
// 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)
|
||
//----------------------------------------------------------------------------------------------------------------------
|
||
|
||
bool TController::BackwardTrackBusy(TTrack *Track)
|
||
{ // najpierw sprawdzamy, czy na danym torze są pojazdy z innego składu
|
||
if( false == Track->Dynamics.empty() ) {
|
||
for( auto dynamic : Track->Dynamics ) {
|
||
if( dynamic->ctOwner != this ) {
|
||
// jeśli jest jakiś cudzy to tor jest zajęty i skanowanie nie obowiązuje
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
return false; // wolny
|
||
};
|
||
|
||
basic_event * TController::CheckTrackEventBackward(double fDirection, TTrack *Track)
|
||
{ // sprawdzanie eventu w torze, czy jest sygnałowym - skanowanie do tyłu
|
||
// NOTE: this method returns only one event which meets the conditions, due to limitations in the caller
|
||
// TBD, TODO: clean up the caller and return all suitable events, as in theory things will go awry if the track has more than one signal
|
||
auto const dir{ pVehicles[ 0 ]->VectorFront() * pVehicles[ 0 ]->DirectionGet() };
|
||
auto const pos{ pVehicles[ 0 ]->HeadPosition() };
|
||
auto const &eventsequence { ( fDirection > 0 ? Track->m_events2 : Track->m_events1 ) };
|
||
for( auto const &event : eventsequence ) {
|
||
if( ( event.second != nullptr )
|
||
&& ( event.second->m_passive )
|
||
&& ( typeid(*(event.second)) == typeid( getvalues_event ) ) ) {
|
||
// since we're checking for events behind us discard the sources in front of the scanning vehicle
|
||
auto const sl{ event.second->input_location() }; // położenie komórki pamięci
|
||
auto const sem{ sl - pos }; // wektor do komórki pamięci od końca składu
|
||
if( dir.x * sem.x + dir.z * sem.z < 0 ) {
|
||
// iloczyn skalarny jest ujemny, gdy sygnał stoi z tyłu
|
||
return event.second;
|
||
}
|
||
}
|
||
}
|
||
return nullptr;
|
||
};
|
||
|
||
TTrack * TController::BackwardTraceRoute(double &fDistance, double &fDirection, TTrack *Track, basic_event *&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 )
|
||
|| ( true == 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, glm::dvec3 const *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( ( OrderCurrentGet() & ~( Shunt | Loose_shunt | Connect ) ) ) {
|
||
// skanowanie sygnałów tylko gdy jedzie w trybie manewrowym albo czeka na rozkazy
|
||
return TCommandType::cm_Unknown;
|
||
}
|
||
// kierunek jazdy względem sprzęgów pojazdu na czele
|
||
int const startdir = -pVehicles[0]->DirectionGet();
|
||
if( startdir == 0 ) {
|
||
// jeśli kabina i kierunek nie jest określony nie robimy nic
|
||
return TCommandType::cm_Unknown;
|
||
}
|
||
// szukamy od pierwszej osi w wybranym kierunku
|
||
double scandir = startdir * pVehicles[0]->RaDirectionGet();
|
||
if (scandir != 0.0) {
|
||
// skanowanie toru w poszukiwaniu eventów GetValues (PutValues nie są przydatne)
|
||
// Ra: przy wstecznym skanowaniu prędkość nie ma znaczenia
|
||
double scanmax = 1000; // 1000m do tyłu, żeby widział przeciwny koniec stacji
|
||
double scandist = scanmax; // zmodyfikuje na rzeczywiście przeskanowane
|
||
basic_event *e = NULL; // event potencjalnie od semafora
|
||
// opcjonalnie może być skanowanie od "wskaźnika" z przodu, np. W5, Tm=Ms1, koniec toru wg drugiej osi w kierunku ruchu
|
||
TTrack *scantrack = BackwardTraceRoute(scandist, scandir, pVehicles[0]->RaTrackGet(), e);
|
||
auto const dir = startdir * pVehicles[0]->VectorFront(); // wektor w kierunku jazdy/szukania
|
||
if( !scantrack ) {
|
||
// jeśli wstecz wykryto koniec toru to raczej nic się nie da w takiej sytuacji zrobić
|
||
return TCommandType::cm_Unknown;
|
||
}
|
||
else {
|
||
// a jeśli są dalej tory
|
||
double vmechmax; // prędkość ustawiona semaforem
|
||
if( e != nullptr ) {
|
||
// jeśli jest jakiś sygnał na widoku
|
||
#if LOGBACKSCAN
|
||
std::string edir {
|
||
"Backward scan by "
|
||
+ pVehicle->asName + " - "
|
||
+ ( ( scandir > 0 ) ?
|
||
"Event2 " :
|
||
"Event1 " ) };
|
||
#endif
|
||
// najpierw sprawdzamy, czy semafor czy inny znak został przejechany
|
||
auto pos = pVehicles[1]->RearPosition(); // pozycja tyłu
|
||
if( typeid( *e ) == typeid( getvalues_event ) )
|
||
{ // przesłać info o zbliżającym się semaforze
|
||
#if LOGBACKSCAN
|
||
edir += "(" + ( e->asNodeName ) + ")";
|
||
#endif
|
||
auto sl = e->input_location(); // położenie komórki pamięci
|
||
auto sem = sl - pos; // wektor do komórki pamięci od końca składu
|
||
if (dir.x * sem.x + dir.z * sem.z < 0) {
|
||
// jeśli został minięty
|
||
// iloczyn skalarny jest ujemny, gdy sygnał stoi z tyłu
|
||
#if LOGBACKSCAN
|
||
WriteLog(edir + " - ignored as not passed yet");
|
||
#endif
|
||
return TCommandType::cm_Unknown; // nic
|
||
}
|
||
vmechmax = e->input_value(1); // prędkość przy tym semaforze
|
||
// przeliczamy odległość od semafora - potrzebne by były współrzędne początku składu
|
||
scandist = sem.Length() - 2; // 2m luzu przy manewrach wystarczy
|
||
if( scandist < 0 ) {
|
||
// ujemnych nie ma po co wysyłać
|
||
scandist = 0;
|
||
}
|
||
bool move = false; // czy AI w trybie manewerowym ma dociągnąć pod S1
|
||
if( e->input_command() == TCommandType::cm_SetVelocity ) {
|
||
if( ( vmechmax == 0.0 ) ?
|
||
( OrderCurrentGet() & ( Shunt | Loose_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 | Loose_shunt ) ) == 0 ) ) ) {
|
||
// 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ł
|
||
#if LOGBACKSCAN
|
||
// WriteLog(edir+"SetProximityVelocity "+AnsiString(scandist) + AnsiString(vmechmax));
|
||
WriteLog(edir);
|
||
#endif
|
||
// SetProximityVelocity(scandist,vmechmax,&sl);
|
||
return (
|
||
vmechmax > 0 ?
|
||
TCommandType::cm_SetVelocity :
|
||
TCommandType::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] ["
|
||
+ to_string( vmechmax, 2 ) + "] ["
|
||
+ to_string( e->Params[ 9 ].asMemCell->Value2(), 2 ) + "]" );
|
||
#endif
|
||
return (
|
||
vmechmax > 0 ?
|
||
TCommandType::cm_SetVelocity :
|
||
TCommandType::cm_Unknown );
|
||
}
|
||
}
|
||
}
|
||
if (OrderCurrentGet() ? OrderCurrentGet() & (Shunt | Loose_shunt | Connect) :
|
||
true) // w Wait_for_orders też widzi tarcze
|
||
{ // reakcja AI w trybie manewrowym dodatkowo na sygnały manewrowe
|
||
if (move ? true : e->input_command() == TCommandType::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) )
|
||
{ // 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) ?
|
||
TCommandType::cm_ChangeDirection :
|
||
TCommandType::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] ["
|
||
+ to_string( vmechmax, 2 ) + "] ["
|
||
+ to_string( e->value( 2 ), 2 ) + "]" );
|
||
#endif
|
||
return (
|
||
vmechmax > 0 ?
|
||
TCommandType::cm_ShuntVelocity :
|
||
TCommandType::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 ?
|
||
TCommandType::cm_ShuntVelocity :
|
||
TCommandType::cm_Unknown );
|
||
}
|
||
} // if (move?...
|
||
} // if (OrderCurrentGet()==Shunt)
|
||
if (e->m_passive) // jeśli skanowany
|
||
if (e->is_command()) // a podłączona komórka ma komendę
|
||
return TCommandType::cm_Command; // to też się obrócić
|
||
} // if (e->Type==tp_GetValues)
|
||
} // if (e)
|
||
} // if (scantrack)
|
||
} // if (scandir!=0.0)
|
||
return TCommandType::cm_Unknown; // nic
|
||
};
|
||
|
||
std::string TController::NextStop() const
|
||
{ // informacja o następnym zatrzymaniu, wyświetlane pod [F1]
|
||
if (asNextStop == "[End of route]")
|
||
return ""; // nie zawiera nazwy stacji, gdy dojechał do końca
|
||
// dodać godzinę odjazdu
|
||
if (!TrainParams)
|
||
return ""; // tu nie powinno nigdy wejść
|
||
std::string nextstop = asNextStop;
|
||
TMTableLine *t = TrainParams->TimeTable + TrainParams->StationIndex;
|
||
if( t->Ah >= 0 ) {
|
||
// przyjazd
|
||
nextstop += " przyj." + std::to_string( t->Ah ) + ":"
|
||
+ ( t->Am < 10 ? "0" : "" ) + std::to_string( t->Am );
|
||
}
|
||
if( t->Dh >= 0 ) {
|
||
// jeśli jest godzina odjazdu
|
||
nextstop += " odj." + std::to_string( t->Dh ) + ":"
|
||
+ ( t->Dm < 10 ? "0" : "" ) + std::to_string( t->Dm );
|
||
}
|
||
return nextstop;
|
||
};
|
||
|
||
void TController::UpdateDelayFlag() {
|
||
|
||
if( TrainParams->CheckTrainLatency() < 0.0 ) {
|
||
// odnotowano spóźnienie
|
||
iDrivigFlags |= moveLate;
|
||
}
|
||
else {
|
||
// przyjazd o czasie
|
||
iDrivigFlags &= ~moveLate;
|
||
}
|
||
}
|
||
|
||
//-----------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
|
||
// conditional below disabled to get around the situation where the AI train does nothing ever
|
||
// because it is waiting for orders which don't come until the engine is engaged, i.e. effectively never
|
||
if (OrderCurrentGet()) // jeśli coś robi
|
||
PrepareEngine(); // niech sprawdzi stan silnika
|
||
else // jeśli nic nie robi
|
||
if (pVehicle->iLights[ ( mvOccupied->CabNo < 0 ?
|
||
end::rear :
|
||
end::front ) ]
|
||
& (light::headlight_left | light::headlight_right | light::headlight_upper)) // któreś ze świateł zapalone?
|
||
{ // od wersji 357 oczekujemy podania komend dla AI przez scenerię
|
||
OrderNext(Prepare_engine);
|
||
if (pVehicle->iLights[mvOccupied->CabNo < 0 ? end::rear : end::front] & light::headlight_upper) // 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
|
||
}
|
||
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)
|
||
ZeroSpeed( true ); // TODO: check if force switch is needed anymore here
|
||
// HACK: make sure the master controller isn't set in position which prevents direction change
|
||
mvControlling->MainCtrlPos = std::min( mvControlling->MainCtrlPos, mvControlling->MaxMainCtrlPosNoDirChange );
|
||
|
||
if( forward ) {
|
||
// do przodu w obecnej kabinie
|
||
while( ( mvOccupied->ActiveDir <= 0 )
|
||
&& ( mvOccupied->DirectionForward() ) ) {
|
||
// force scan table update
|
||
iTableDirection = 0;
|
||
}
|
||
}
|
||
else {
|
||
// do tyłu w obecnej kabinie
|
||
while( ( mvOccupied->ActiveDir >= 0 )
|
||
&& ( mvOccupied->DirectionBackward() ) ) {
|
||
// force scan table update
|
||
iTableDirection = 0;
|
||
}
|
||
}
|
||
if( mvOccupied->TrainType == dt_SN61 ) {
|
||
// specjalnie dla SN61 żeby nie zgasł
|
||
while( ( mvControlling->RList[ mvControlling->MainCtrlPos ].Mn == 0 )
|
||
&& ( mvControlling->IncMainCtrl( 1 ) ) ) {
|
||
; // all work is done in the header
|
||
}
|
||
}
|
||
}
|
||
|
||
void TController::ZeroDirection() {
|
||
|
||
while( ( mvOccupied->ActiveDir > 0 ) && ( mvOccupied->DirectionBackward() ) ) { ; }
|
||
while( ( mvOccupied->ActiveDir < 0 ) && ( mvOccupied->DirectionForward() ) ) { ; }
|
||
}
|
||
|
||
Mtable::TTrainParameters const *
|
||
TController::TrainTimetable() const {
|
||
return TrainParams;
|
||
}
|
||
|
||
std::string TController::Relation() const
|
||
{ // zwraca relację pociągu
|
||
return TrainParams->ShowRelation();
|
||
};
|
||
|
||
std::string TController::TrainName() const
|
||
{ // zwraca numer pociągu
|
||
return TrainParams->TrainName;
|
||
};
|
||
|
||
int TController::StationCount() const
|
||
{ // zwraca ilość stacji (miejsc zatrzymania)
|
||
return TrainParams->StationCount;
|
||
};
|
||
|
||
int TController::StationIndex() const
|
||
{ // zwraca indeks aktualnej stacji (miejsca zatrzymania)
|
||
return TrainParams->StationIndex;
|
||
};
|
||
|
||
bool TController::IsStop() const
|
||
{ // informuje, czy jest zatrzymanie na najbliższej stacji
|
||
return TrainParams->IsStop();
|
||
};
|
||
|
||
// returns most recently calculated distance to potential obstacle ahead
|
||
double
|
||
TController::TrackBlock() const {
|
||
|
||
return Obstacle.distance;
|
||
}
|
||
|
||
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
|
||
};
|
||
|
||
std::string TController::TableText( std::size_t const Index ) const
|
||
{ // pozycja tabelki prędkości
|
||
if( Index < sSpeedTable.size() ) {
|
||
return sSpeedTable[ Index ].TableText();
|
||
}
|
||
else {
|
||
return "";
|
||
}
|
||
};
|
||
|
||
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( std::size_t i = 0; i < sSpeedTable.size(); ++i )
|
||
{ // 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( ( true == TestFlag( sSpeedTable[ i ].iFlags, spEnabled | spTrack ) )
|
||
&& ( 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 != 0 )
|
||
&& ( iRouteWanted != d ) ) { // nowy kierunek
|
||
iRouteWanted = d; // zapamiętanie
|
||
if( mvOccupied->CategoryFlag & 2 ) {
|
||
// jeśli samochód
|
||
for( std::size_t i = 0; i < sSpeedTable.size(); ++i ) {
|
||
// szukanie pierwszego skrzyżowania i resetowanie kierunku na nim
|
||
if( true == TestFlag( sSpeedTable[ i ].iFlags, spEnabled | spTrack ) ) {
|
||
// jeśli pozycja istotna (1) oraz odcinek (2)
|
||
if( false == TestFlag( sSpeedTable[ i ].iFlags, spElapsed ) ) {
|
||
// odcinek nie może być miniętym
|
||
if( sSpeedTable[ i ].trTrack->eType == tt_Cross ) // jeśli skrzyżowanie
|
||
{
|
||
while( sSpeedTable.size() >= i ) {
|
||
// NOTE: we're ignoring semaphor flags and not resetting them like we do for train route trimming
|
||
// but what if there's street lights?
|
||
// TODO: investigate
|
||
sSpeedTable.pop_back();
|
||
}
|
||
iLast = sSpeedTable.size();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
};
|
||
*/
|
||
std::string TController::OwnerName() const
|
||
{
|
||
return ( pVehicle ? pVehicle->MoverParameters->Name : "none" );
|
||
};
|