16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-22 23:19:19 +02:00

Merge branch 'gfx-work' into sim

This commit is contained in:
milek7
2019-07-13 01:47:36 +02:00
34 changed files with 1304 additions and 688 deletions

View File

@@ -1390,6 +1390,12 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
fVelDes = v;
}
}
if( ( true == TestFlag( 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;
}
}
else if (sSpeedTable[i].iFlags & spTrack) // jeśli tor
{ // tor ogranicza prędkość, dopóki cały skład nie przejedzie,
@@ -1401,12 +1407,6 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN
// 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
}
@@ -2129,7 +2129,10 @@ bool TController::CheckVehicles(TOrders user)
// 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
}
// enable heating and converter in carriages with can be heated
// NOTE: don't touch the controlled vehicle, let the user/ai handle it explicitly
if( p->MoverParameters != mvControlling ) {
if( p->MoverParameters->HeatingPower > 0 ) {
p->MoverParameters->HeatingAllow = true;
p->MoverParameters->ConverterSwitch( true, range_t::local );
@@ -2264,6 +2267,12 @@ bool TController::CheckVehicles(TOrders user)
// zmiana czoła przez manewry
iDrivigFlags &= ~movePushPull;
}
if( ( user == Connect )
|| ( user == Disconnect ) ) {
// HACK: force route table update on consist change, new consist length means distances to points of interest are now wrong
iTableDirection = 0;
}
} // blok wykonywany, gdy aktywnie prowadzi
return true;
}
@@ -2452,7 +2461,7 @@ bool TController::PrepareEngine()
mvOccupied->PantRear( true );
if (mvControlling->PantPress < 4.2) {
// załączenie małej sprężarki
if( mvControlling->TrainType != dt_EZT ) {
if( false == mvControlling->PantAutoValve ) {
// odłączenie zbiornika głównego, bo z nim nie da rady napompować
mvControlling->bPantKurek3 = false;
}
@@ -2525,14 +2534,14 @@ bool TController::PrepareEngine()
}
else {
OK = ( OrderDirectionChange( iDirection, mvOccupied ) == -1 );
mvOccupied->ConverterSwitch( true );
mvControlling->ConverterSwitch( true );
// w EN57 sprężarka w ra jest zasilana z silnikowego
mvOccupied->CompressorSwitch( true );
mvControlling->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 );
mvControlling->MotorBlowersSwitchOff( false, end::front );
mvControlling->MotorBlowersSwitch( true, end::front );
mvControlling->MotorBlowersSwitchOff( false, end::rear );
mvControlling->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 ) );
@@ -2546,6 +2555,14 @@ bool TController::PrepareEngine()
if( lookup != brakepositions.end() ) {
BrakeLevelSet( lookup->second ); // GBH
}
// enable train heating
// HACK: to account for su-45/-46 shortcomings diesel-powered engines only activate heating in cold conditions
// TODO: take instead into account presence of converters in attached cars, once said presence is possible to specify
mvControlling->HeatingAllow = (
( ( mvControlling->EngineType == TEngineType::DieselElectric )
|| ( mvControlling->EngineType == TEngineType::DieselEngine ) ) ?
( Global.AirTemperature < 10 ) :
true );
}
}
else
@@ -2628,7 +2645,9 @@ bool TController::ReleaseEngine() {
mvOccupied->OperateDoors( side::left, false );
if( true == mvControlling->Mains ) {
mvControlling->CompressorSwitch( false );
// heating
mvControlling->HeatingAllow = false;
// devices
mvControlling->ConverterSwitch( false );
// line breaker/engine
OK = mvControlling->MainSwitch( false );
@@ -3123,6 +3142,14 @@ bool TController::IncSpeed()
{ // dla 2Ls150 można zmienić tryb pracy, jeśli jest w liniowym i nie daje rady (wymaga zerowania kierunku)
// mvControlling->ShuntMode=(OrderList[OrderPos]&Shunt)||(fMass>224000.0);
}
if (mvControlling->EIMCtrlType > 0) {
if (true == Ready)
{
DizelPercentage = (mvControlling->Vel > mvControlling->dizel_minVelfullengage ? 100 : 1);
}
break;
}
else
if( true == Ready ) {
if( ( mvControlling->Vel > mvControlling->dizel_minVelfullengage )
&& ( mvControlling->RList[ mvControlling->MainCtrlPos ].Mn > 0 ) ) {
@@ -3185,6 +3212,12 @@ bool TController::DecSpeed(bool force)
mvControlling->DecMainCtrl(3 + 3 * floor(0.5 + fabs(AccDesired)));
break;
case TEngineType::DieselEngine:
if (mvControlling->EIMCtrlType > 0)
{
DizelPercentage = 0;
break;
}
if ((mvControlling->Vel > mvControlling->dizel_minVelfullengage))
{
if (mvControlling->RList[mvControlling->MainCtrlPos].Mn > 0)
@@ -3508,6 +3541,64 @@ void TController::SetTimeControllers()
}
}
//5. Check Main Controller in Dizels
//5.1. Digital controller in DMUs with hydro
if ((mvControlling->EngineType == TEngineType::DieselEngine) && (mvControlling->EIMCtrlType == 3))
{
DizelPercentage_Speed = DizelPercentage; //wstepnie procenty
auto MinVel{ std::min(mvControlling->hydro_TC_LockupSpeed, mvControlling->Vmax / 6) }; //minimal velocity
if (VelDesired > MinVel) //more power for faster ride
{
auto const Factor{ 10 * (mvControlling->Vmax) / (mvControlling->Vmax + 3 * mvControlling->Vel) };
auto DesiredPercentage{ clamp(
(VelDesired > mvControlling->Vel ?
(VelDesired - mvControlling->Vel) / Factor :
0),
0.0, 1.0) }; //correction for reaching desired velocity
if ((VelDesired < 0.5 * mvControlling->Vmax) //low velocity and reaching desired
&& (VelDesired - mvControlling->Vel < 10)) {
DesiredPercentage = std::min(DesiredPercentage, 0.75);
}
DizelPercentage_Speed = std::round(DesiredPercentage * DizelPercentage);
}
else
{
DizelPercentage_Speed = std::min(DizelPercentage, mvControlling->Vel < 0.99 * VelDesired ? 1 : 0);
}
auto const DizelActualPercentage { int(100.4 * mvControlling->eimic_real) };
auto const PosInc { mvControlling->MainCtrlPosNo };
auto PosDec { 0 };
for( int i = PosInc; i >= 0; --i ) {
if( ( mvControlling->UniCtrlList[ i ].SetCtrlVal <= 0 )
&& ( mvControlling->UniCtrlList[ i ].SpeedDown > 0.01 ) ) {
PosDec = i;
break;
}
}
if( std::abs( DizelPercentage_Speed - DizelActualPercentage ) > ( DizelPercentage > 1 ? 0 : 0 ) ) {
if( ( PosDec > 0 )
&& ( ( DizelActualPercentage - DizelPercentage_Speed > 50 )
|| ( ( DizelPercentage_Speed == 0 )
&& ( DizelActualPercentage > 10 ) ) ) ) {
//one position earlier should be fast decreasing
PosDec -= 1;
}
auto const DesiredPos { (
DizelPercentage_Speed > DizelActualPercentage ?
PosInc :
PosDec ) };
while( mvControlling->MainCtrlPos > DesiredPos ) { mvControlling->DecMainCtrl( 1 ); }
while( mvControlling->MainCtrlPos < DesiredPos ) { mvControlling->IncMainCtrl( 1 ); }
}
if (BrakeCtrlPosition < 0.1) //jesli nie hamuje
mvOccupied->BrakeLevelSet(mvControlling->UniCtrlList[mvControlling->MainCtrlPos].mode); //zeby nie bruzdzilo machanie zespolonym
}
else
//5.2. Analog direct controller
if ((mvControlling->EngineType == TEngineType::DieselEngine)&&(mvControlling->Vmax>30))
{
int MaxPos = mvControlling->MainCtrlPosNo;
@@ -3567,6 +3658,28 @@ void TController::CheckTimeControllers()
mvOccupied->ScndCtrlPos = 2;
}
}
//5. Check Main Controller in Dizels
//5.1. Digital controller in DMUs with hydro
if ((mvControlling->EngineType == TEngineType::DieselEngine) && (mvControlling->EIMCtrlType == 3))
{
int DizelActualPercentage = 100.4 * mvControlling->eimic_real;
int NeutralPos = mvControlling->MainCtrlPosNo - 1; //przedostatnia powinna wstrzymywać - hipoteza robocza
for (int i = mvControlling->MainCtrlPosNo; i >= 0; i--)
if ((mvControlling->UniCtrlList[i].SetCtrlVal <= 0) && (mvControlling->UniCtrlList[i].SpeedDown < 0.01)) //niby zero, ale nie zmniejsza procentów
{
NeutralPos = i;
break;
}
if (BrakeCtrlPosition < 0.1) //jesli nie hamuje
{
if ((DizelActualPercentage >= DizelPercentage_Speed) && (mvControlling->MainCtrlPos > NeutralPos))
while (mvControlling->MainCtrlPos > NeutralPos) mvControlling->DecMainCtrl(1);
if ((DizelActualPercentage <= DizelPercentage_Speed) && (mvControlling->MainCtrlPos < NeutralPos))
while (mvControlling->MainCtrlPos < NeutralPos) mvControlling->IncMainCtrl(1);
mvOccupied->BrakeLevelSet(mvControlling->UniCtrlList[mvControlling->MainCtrlPos].mode); //zeby nie bruzdzilo machanie zespolonym
}
}
};
// otwieranie/zamykanie drzwi w składzie albo (tylko AI) EZT
@@ -4748,7 +4861,7 @@ TController::UpdateSituation(double dt) {
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
CheckVehicles( Connect ); // sprawdzić światła nowego składu
JumpToNextOrder(); // wykonanie następnej komendy
}
}
@@ -5115,6 +5228,7 @@ TController::UpdateSituation(double dt) {
// tylko jeśli odepnie
WriteLog( mvOccupied->Name + " odczepiony." );
iVehicleCount = -2;
CheckVehicles( Disconnect ); // update trainset state
} // a jak nie, to dociskać dalej
}
if ((mvOccupied->Vel < 0.01) && !(iDrivigFlags & movePress))
@@ -5122,6 +5236,7 @@ TController::UpdateSituation(double dt) {
// za radą yB ustawiamy pozycję 3 kranu (ruszanie kranem w innych miejscach
// powino zostać wyłączone)
// WriteLog("Zahamowanie składu");
AccDesired = std::min( AccDesired, -0.9 ); // HACK: make sure the ai doesn't try to release the brakes to accelerate
if( mvOccupied->BrakeSystem == TBrakeSystem::ElectroPneumatic ) {
mvOccupied->BrakeLevelSet( mvOccupied->Handle->GetPos( bh_EPB ) );
}
@@ -5369,7 +5484,8 @@ TController::UpdateSituation(double dt) {
exchangetime = std::max( exchangetime, vehicle->LoadExchangeTime() );
vehicle = vehicle->Next();
}
if( exchangetime > 0 ) {
if( ( exchangetime > 0 )
|| ( mvOccupied->Vel > 2.0 ) ) { // HACK: force timer reset if the load exchange is cancelled due to departure
WaitingSet( exchangetime );
}
}

View File

@@ -265,6 +265,8 @@ public:
int iOverheadZero = 0; // suma bitowa jezdy bezprądowej, bity ustawiane przez pojazdy z podniesionymi pantografami
int iOverheadDown = 0; // suma bitowa opuszczenia pantografów, bity ustawiane przez pojazdy z podniesionymi pantografami
double BrakeCtrlPosition = 0.0; // intermediate position of main brake controller
int DizelPercentage = 0; // oczekiwane procenty jazdy/hamowania szynobusem
int DizelPercentage_Speed = 0; // oczekiwane procenty jazdy/hamowania szynobusem w związku z osiąganiem VelDesired
private:
bool Psyche = false;
int HelperState = 0; //stan pomocnika maszynisty

View File

@@ -2720,10 +2720,11 @@ bool TDynamicObject::Update(double dt, double dt1)
if (v == 0.0) {
v = MoverParameters->PantFrontVolt;
if( v == 0.0 ) {
if( MoverParameters->TrainType & ( dt_EZT | dt_ET40 | dt_ET41 | dt_ET42 ) ) {
// if( MoverParameters->TrainType & ( dt_EZT | dt_ET40 | dt_ET41 | dt_ET42 ) ) {
// dwuczłony mogą mieć sprzęg WN
// NOTE: condition disabled, other vehicles types can have power cables as well
v = MoverParameters->GetTrainsetVoltage(); // ostatnia szansa
}
// }
}
}
if (v != 0.0)
@@ -2776,7 +2777,14 @@ bool TDynamicObject::Update(double dt, double dt1)
if (Mechanik)
{ // Ra 2F3F: do Driver.cpp to przenieść?
MoverParameters->EqvtPipePress = GetEPP(); // srednie cisnienie w PG
if( ( Mechanik->Primary() )
if ((Mechanik->Primary())
&& (MoverParameters->EngineType == TEngineType::DieselEngine)
&& (MoverParameters->EIMCtrlType > 0)) {
MoverParameters->CheckEIMIC(dt1);
MoverParameters->eimic_real = MoverParameters->eimic;
MoverParameters->SendCtrlToNext("EIMIC", MoverParameters->eimic, MoverParameters->CabNo);
}
if( ( Mechanik->Primary() )
&& ( MoverParameters->EngineType == TEngineType::ElectricInductionMotor ) ) {
// jesli glowny i z asynchronami, to niech steruje hamulcem i napedem lacznie dla calego pociagu/ezt
auto const kier = (DirectionGet() * MoverParameters->ActiveCab > 0);
@@ -5828,23 +5836,28 @@ TDynamicObject::LoadMMediaFile_mdload( std::string const &Name ) const {
TModel3d *loadmodel { nullptr };
// check if we don't have model override for this load type
auto const lookup { LoadModelOverrides.find( Name ) };
if( lookup != LoadModelOverrides.end() ) {
loadmodel = TModelsManager::GetModel( asBaseDir + lookup->second, true );
// if the override was succesfully loaded call it a day
if( loadmodel != nullptr ) { return loadmodel; }
{
auto const lookup { LoadModelOverrides.find( Name ) };
if( lookup != LoadModelOverrides.end() ) {
loadmodel = TModelsManager::GetModel( asBaseDir + lookup->second, true );
// if the override was succesfully loaded call it a day
if( loadmodel != nullptr ) { return loadmodel; }
}
}
// regular routine if there's no override or it couldn't be loaded
// try first specialized version of the load model, vehiclename_loadname
auto const specializedloadfilename { asBaseDir + MoverParameters->TypeName + "_" + Name };
if( ( true == FileExists( specializedloadfilename + ".e3d" ) )
|| ( true == FileExists( specializedloadfilename + ".t3d" ) ) ) {
loadmodel = TModelsManager::GetModel( specializedloadfilename, true );
{
auto const specializedloadfilename { asBaseDir + MoverParameters->TypeName + "_" + Name };
loadmodel = TModelsManager::GetModel( specializedloadfilename, true, false );
if( loadmodel != nullptr ) { return loadmodel; }
}
if( loadmodel == nullptr ) {
// if this fails, try generic load model
loadmodel = TModelsManager::GetModel( asBaseDir + Name, true );
// try generic version of the load model next, loadname
{
auto const genericloadfilename { asBaseDir + Name };
loadmodel = TModelsManager::GetModel( genericloadfilename, true, false );
if( loadmodel != nullptr ) { return loadmodel; }
}
// if we're still here, give up
return loadmodel;
}
@@ -6689,7 +6702,7 @@ TDynamicObject::powertrain_sounds::render( TMoverParameters const &Vehicle, doub
// youBy - przenioslem, bo diesel tez moze miec turbo
if( Vehicle.TurboTest > 0 ) {
// udawanie turbo:
auto const pitch_diesel { Vehicle.EngineType == TEngineType::DieselEngine ? Vehicle.enrot / Vehicle.dizel_nmax : 0 };
auto const pitch_diesel { Vehicle.EngineType == TEngineType::DieselEngine ? Vehicle.enrot / Vehicle.dizel_nmax : 1 };
auto const goalpitch { std::max( 0.025, ( engine_volume * pitch_diesel + engine_turbo.m_frequencyoffset ) * engine_turbo.m_frequencyfactor ) };
auto const goalvolume { (
( ( Vehicle.MainCtrlPos >= Vehicle.TurboTest ) && ( Vehicle.enrot > 0.1 ) ) ?

View File

@@ -59,12 +59,25 @@ bool TEventLauncher::Load(cParser *parser)
dRadius *= dRadius; // do kwadratu, pod warunkiem, że nie jest ujemne
parser->getTokens(); // klawisz sterujący
*parser >> token;
if (token != "none")
{
if( token.size() == 1 )
if (token != "none") {
if( token.size() == 1 ) {
// single char, assigned activation key
iKey = vk_to_glfw_key( token[ 0 ] );
else
iKey = vk_to_glfw_key(stol_def( token, 0 )); // a jak więcej, to jakby numer klawisza jest
}
else {
// this launcher may be activated by radio message
std::map<std::string, int> messages {
{ "radio_call1", radio_message::call1 },
{ "radio_call3", radio_message::call3 }
};
auto lookup = messages.find( token );
iKey = (
lookup != messages.end() ?
lookup->second :
// a jak więcej, to jakby numer klawisza jest
vk_to_glfw_key( stol_def( token, 0 ) ) );
}
}
parser->getTokens();
*parser >> DeltaTime;
@@ -139,6 +152,8 @@ bool TEventLauncher::Load(cParser *parser)
}
bool TEventLauncher::check_activation_key() {
if (iKey <= 0)
return false;
char key = iKey & 0xff;
@@ -146,9 +161,9 @@ bool TEventLauncher::check_activation_key() {
char modifier = iKey >> 8;
if (modifier & GLFW_MOD_SHIFT)
result |= Global.shiftState;
result &= Global.shiftState;
if (modifier & GLFW_MOD_CONTROL)
result |= Global.ctrlState;
result &= Global.ctrlState;
return result;
}
@@ -213,6 +228,11 @@ bool TEventLauncher::IsGlobal() const {
&& ( dRadius < 0.0 ) ); // bez ograniczenia zasięgu
}
bool TEventLauncher::IsRadioActivated() const {
return ( iKey < 0 );
}
// radius() subclass details, calculates node's bounding radius
float
TEventLauncher::radius_() {

View File

@@ -14,6 +14,12 @@ http://mozilla.org/MPL/2.0/.
#include "Classes.h"
#include "scenenode.h"
// radio-transmitted event launch messages
enum radio_message {
call3 = -3,
call1 = -1
};
class TEventLauncher : public scene::basic_node {
public:
@@ -28,7 +34,11 @@ public:
bool check_activation();
// checks conditions associated with the event. returns: true if the conditions are met
bool check_conditions();
inline
auto key() const {
return iKey; }
bool IsGlobal() const;
bool IsRadioActivated() const;
// members
std::string asEvent1Name;
std::string asEvent2Name;

View File

@@ -1034,7 +1034,7 @@ logvalues_event::export_as_text_( std::ostream &Output ) const {
void
multi_event::init() {
auto const conditiontchecksmemcell { m_conditions.flags & ( flags::text | flags::value_1 | flags::value_2 ) };
auto const conditiontchecksmemcell { ( m_conditions.flags & ( flags::text | flags::value_1 | flags::value_2 ) ) != 0 };
// not all multi-events have memory cell checks, for the ones which don't we can keep quiet about it
init_targets( simulation::Memory, "memory cell", conditiontchecksmemcell );
if( m_ignored ) {
@@ -1964,6 +1964,22 @@ event_manager::queue( TEventLauncher *Launcher ) {
m_launcherqueue.emplace_back( Launcher );
}
// inserts in the event query events assigned to event launchers capable of receiving specified radio message sent from specified location
void
event_manager::queue_receivers( radio_message const Message, glm::dvec3 const &Location ) {
for( auto *launcher : m_radiodrivenlaunchers.sequence() ) {
if( ( launcher->key() == Message )
&& ( ( launcher->dRadius < 0 )
|| ( glm::length2( launcher->location() - Location ) < launcher->dRadius ) )
&& ( true == launcher->check_conditions() ) ) {
// NOTE: only execution of event1 is supported for radio messages
// TBD, TODO: consider ability/way to execute event2
simulation::Events.AddToQuery( launcher->Event1, nullptr );
}
}
}
// legacy method, updates event queues
void
event_manager::update() {
@@ -2187,32 +2203,39 @@ event_manager::InitEvents() {
void
event_manager::InitLaunchers() {
for( auto *launcher : m_launchers.sequence() ) {
std::vector<basic_table<TEventLauncher> *> launchertables {
&m_inputdrivenlaunchers,
&m_radiodrivenlaunchers
};
if( launcher->iCheckMask != 0 ) {
if( launcher->asMemCellName != "none" ) {
// jeśli jest powiązana komórka pamięci
launcher->MemCell = simulation::Memory.find( launcher->asMemCellName );
if( launcher->MemCell == nullptr ) {
ErrorLog( "Bad scenario: event launcher \"" + launcher->name() + "\" can't find memcell \"" + launcher->asMemCellName + "\"" );
for( auto *launchertable : launchertables ) {
for( auto *launcher : launchertable->sequence() ) {
if( launcher->iCheckMask != 0 ) {
if( launcher->asMemCellName != "none" ) {
// jeśli jest powiązana komórka pamięci
launcher->MemCell = simulation::Memory.find( launcher->asMemCellName );
if( launcher->MemCell == nullptr ) {
ErrorLog( "Bad scenario: event launcher \"" + launcher->name() + "\" can't find memcell \"" + launcher->asMemCellName + "\"" );
}
}
else {
launcher->MemCell = nullptr;
}
}
else {
launcher->MemCell = nullptr;
}
}
if( launcher->asEvent1Name != "none" ) {
launcher->Event1 = simulation::Events.FindEvent( launcher->asEvent1Name );
if( launcher->Event1 == nullptr ) {
ErrorLog( "Bad scenario: event launcher \"" + launcher->name() + "\" can't find event \"" + launcher->asEvent1Name + "\"" );
if( launcher->asEvent1Name != "none" ) {
launcher->Event1 = simulation::Events.FindEvent( launcher->asEvent1Name );
if( launcher->Event1 == nullptr ) {
ErrorLog( "Bad scenario: event launcher \"" + launcher->name() + "\" can't find event \"" + launcher->asEvent1Name + "\"" );
}
}
}
if( launcher->asEvent2Name != "none" ) {
launcher->Event2 = simulation::Events.FindEvent( launcher->asEvent2Name );
if( launcher->Event2 == nullptr ) {
ErrorLog( "Bad scenario: event launcher \"" + launcher->name() + "\" can't find event \"" + launcher->asEvent2Name + "\"" );
if( launcher->asEvent2Name != "none" ) {
launcher->Event2 = simulation::Events.FindEvent( launcher->asEvent2Name );
if( launcher->Event2 == nullptr ) {
ErrorLog( "Bad scenario: event launcher \"" + launcher->name() + "\" can't find event \"" + launcher->asEvent2Name + "\"" );
}
}
}
}
@@ -2228,8 +2251,14 @@ event_manager::export_as_text( std::ostream &Output ) const {
event->export_as_text( Output );
}
}
Output << "// event launchers\n";
for( auto const *launcher : m_launchers.sequence() ) {
Output << "// event launchers, basic\n";
for( auto const *launcher : m_inputdrivenlaunchers.sequence() ) {
if( launcher->group() == null_handle ) {
launcher->export_as_text( Output );
}
}
Output << "// event launchers, radio driven\n";
for( auto const *launcher : m_radiodrivenlaunchers.sequence() ) {
if( launcher->group() == null_handle ) {
launcher->export_as_text( Output );
}
@@ -2239,7 +2268,7 @@ event_manager::export_as_text( std::ostream &Output ) const {
std::vector<TEventLauncher*> event_manager::find_eventlaunchers(glm::vec2 center, float radius) const {
std::vector<TEventLauncher *> results;
for (auto &launcher : m_launchers.sequence()) {
for (auto &launcher : m_inputdrivenlaunchers.sequence()) {
glm::dvec3 location = launcher->location();
if (glm::distance2(glm::vec2(location.x, location.z), center) < radius)
results.push_back(launcher);

20
Event.h
View File

@@ -578,6 +578,9 @@ public:
// adds specified event launcher to the list of global launchers
void
queue( TEventLauncher *Launcher );
// inserts in the event query events assigned to event launchers capable of receiving specified radio message sent from specified location
void
queue_receivers( radio_message const Message, glm::dvec3 const &Location );
// legacy method, updates event queues
void
update();
@@ -588,10 +591,13 @@ public:
inline
bool
insert( TEventLauncher *Launcher ) {
return m_launchers.insert( Launcher ); }
inline void purge (TEventLauncher *Launcher) {
m_launchers.purge(Launcher);
}
return (
Launcher->IsRadioActivated() ?
m_radiodrivenlaunchers.insert( Launcher ) :
m_inputdrivenlaunchers.insert( Launcher ) ); }
inline void purge (TEventLauncher *Launcher) {
m_radiodrivenlaunchers.purge(Launcher);
m_inputdrivenlaunchers.purge(Launcher); }
// returns first event in the queue
inline
basic_event *
@@ -607,7 +613,8 @@ public:
basic_event *
FindEvent( std::string const &Name );
inline TEventLauncher* FindEventlauncher(std::string const &Name) {
return m_launchers.find(Name);
auto ptr = m_inputdrivenlaunchers.find(Name);
return ptr ? ptr : m_radiodrivenlaunchers.find(Name);
}
// legacy method, inserts specified event in the event query
bool
@@ -643,7 +650,8 @@ private:
basic_event *QueryRootEvent { nullptr };
basic_event *m_workevent { nullptr };
event_map m_eventmap;
basic_table<TEventLauncher> m_launchers;
basic_table<TEventLauncher> m_inputdrivenlaunchers;
basic_table<TEventLauncher> m_radiodrivenlaunchers;
eventlauncher_sequence m_launcherqueue;
command_relay m_relay;
};

View File

@@ -183,6 +183,7 @@ TGauge::Load_mapping( cParser &Input ) {
m_type = (
gaugetype == "impulse" ? TGaugeType::push :
gaugetype == "return" ? TGaugeType::push :
gaugetype == "delayed" ? TGaugeType::push_delayed :
TGaugeType::toggle ); // default
}
else if( key == "soundinc:" ) {

View File

@@ -23,7 +23,8 @@ enum class TGaugeAnimation {
enum class TGaugeType {
toggle,
push
push,
push_delayed
};
// animowany wskaźnik, mogący przyjmować wiele stanów pośrednich

View File

@@ -59,7 +59,7 @@ global_settings::ConfigParse(cParser &Parser) {
Parser.getTokens( 1, false );
Parser >> FieldOfView;
// guard against incorrect values
FieldOfView = clamp( FieldOfView, 15.0f, 75.0f );
FieldOfView = clamp( FieldOfView, 10.0f, 75.0f );
}
else if (token == "width")
{

View File

@@ -377,7 +377,7 @@ struct TBrakePressure
typedef std::map<int,TBrakePressure> TBrakePressureTable;
/*typy napedow*/
enum class TEngineType { None, Dumb, WheelsDriven, ElectricSeriesMotor, ElectricInductionMotor, DieselEngine, SteamEngine, DieselElectric };
enum class TEngineType { None, Dumb, WheelsDriven, ElectricSeriesMotor, ElectricInductionMotor, DieselEngine, SteamEngine, DieselElectric, Main };
/*postac dostarczanej energii*/
enum class TPowerType { NoPower, BioPower, MechPower, ElectricPower, SteamPower };
/*rodzaj paliwa*/
@@ -431,10 +431,22 @@ struct TCurrentCollector {
//}
};
/*typy źródeł mocy*/
enum class TPowerSource { NotDefined, InternalSource, Transducer, Generator, Accumulator, CurrentCollector, PowerCable, Heater };
enum class TPowerSource { NotDefined, InternalSource, Transducer, Generator, Accumulator, CurrentCollector, PowerCable, Heater, Main };
struct engine_generator {
// ld inputs
double *engine_revolutions; // revs per second of the prime mover
// config
double revolutions_min; // min working revolutions rate, in revs per second
double revolutions_max; // max working revolutions rate, in revs per second
double voltage_min; // voltage generated at min working revolutions
double voltage_max; // voltage generated at max working revolutions
// ld outputs
double revolutions;
double voltage;
};
struct _mover__1
struct TAccumulator
{
double MaxCapacity;
TPowerSource RechargeSource;
@@ -444,7 +456,7 @@ struct _mover__1
//}
};
struct _mover__2
struct TPowerCable
{
TPowerType PowerTrans;
double SteamPressure;
@@ -454,12 +466,17 @@ struct _mover__2
//}
};
struct _mover__3
struct THeater
{
TGrateType Grate;
TBoilerType Boiler;
};
struct TTransducer {
// ld inputs
double InputVoltage;
};
/*parametry źródeł mocy*/
struct TPowerParameters
{
@@ -471,11 +488,11 @@ struct TPowerParameters
{
struct
{
_mover__3 RHeater;
THeater RHeater;
};
struct
{
_mover__2 RPowerCable;
TPowerCable RPowerCable;
};
struct
{
@@ -483,15 +500,15 @@ struct TPowerParameters
};
struct
{
_mover__1 RAccumulator;
TAccumulator RAccumulator;
};
struct
{
TEngineType GeneratorEngine;
engine_generator EngineGenerator;
};
struct
{
double InputVoltage;
TTransducer Transducer;
};
struct
{
@@ -633,7 +650,8 @@ enum class TCouplerType { NoCoupler, Articulated, Bare, Chain, Screw, Automatic
struct power_coupling {
double current{ 0.0 };
double voltage{ 0.0 };
bool local{ false }; // whether the power comes from external or onboard source
bool is_local{ false }; // whether the power comes from external or onboard source
bool is_live{ false }; // whether the coupling with next vehicle is live
};
struct TCoupling {
@@ -1037,7 +1055,11 @@ public:
/* dizel_auto_min, dizel_auto_max: real; {predkosc obrotowa przelaczania automatycznej skrzyni biegow*/
double dizel_nmax_cutoff = 0.0; /*predkosc obrotowa zadzialania ogranicznika predkosci*/
double dizel_nmin = 0.0; /*najmniejsza dopuszczalna predkosc obrotowa*/
double dizel_nmin_hdrive = 0.0; /*najmniejsza dopuszczalna predkosc obrotowa w czasie jazdy na hydro */
double dizel_nmin_hdrive_factor = 0.0; /*wspolczynnik wzrostu obrotow minimalnych hydro zaleznosci od zadanego procentu*/
double dizel_nmin_retarder = 0.0; /*obroty pracy podczas hamowania retarderem*/
double dizel_minVelfullengage = 0.0; /*najmniejsza predkosc przy jezdzie ze sprzeglem bez poslizgu*/
double dizel_maxVelANS = 3.0; /*predkosc progowa rozlaczenia przetwornika momentu*/
double dizel_AIM = 1.0; /*moment bezwladnosci walu itp*/
double dizel_engageDia = 0.5; double dizel_engageMaxForce = 6000.0; double dizel_engagefriction = 0.5; /*parametry sprzegla*/
double engagedownspeed = 0.9;
@@ -1065,6 +1087,7 @@ public:
double hydro_R_FillRateInc = 1.0; /*szybkosc napelniania sprzegla*/
double hydro_R_FillRateDec = 1.0; /*szybkosc oprozniania sprzegla*/
double hydro_R_MinVel = 1.0; /*minimalna predkosc, przy ktorej retarder dziala*/
double hydro_R_EngageVel = 1.0; /*minimalna predkosc hamowania, przy ktorej sprzeglo jest wciaz wlaczone*/
/*- dla lokomotyw spalinowo-elektrycznych -*/
double AnPos = 0.0; // pozycja sterowania dokladnego (analogowego)
bool AnalogCtrl = false; //
@@ -1096,10 +1119,10 @@ public:
double MED_EPVC_Time = 7; // czas korekcji sily hamowania EP, gdy nie ma dostepnego ED
bool MED_Ncor = 0; // czy korekcja sily hamowania z uwzglednieniem nacisku
int DCEMUED_CC; //na którym sprzęgu sprawdzać działanie ED
double DCEMUED_EP_max_Vel; //maksymalna prędkość, przy której działa EP przy włączonym ED w jednostce (dla tocznych)
double DCEMUED_EP_min_Im; //minimalny prąd, przy którym EP nie działa przy włączonym ED w członie (dla silnikowych)
double DCEMUED_EP_delay; //opóźnienie włączenia hamulca EP przy hamowaniu ED - zwłoka wstępna
int DCEMUED_CC { 0 }; //na którym sprzęgu sprawdzać działanie ED
double DCEMUED_EP_max_Vel{ 0.0 }; //maksymalna prędkość, przy której działa EP przy włączonym ED w jednostce (dla tocznych)
double DCEMUED_EP_min_Im{ 0.0 }; //minimalny prąd, przy którym EP nie działa przy włączonym ED w członie (dla silnikowych)
double DCEMUED_EP_delay{ 0.0 }; //opóźnienie włączenia hamulca EP przy hamowaniu ED - zwłoka wstępna
/*-dla wagonow*/
struct load_attributes {
@@ -1240,6 +1263,8 @@ public:
/*-zmienne dla lokomotyw*/
bool Mains = false; /*polozenie glownego wylacznika*/
double MainsInitTime{ 0.0 }; // config, initialization time (in seconds) of the main circuit after it receives power, before it can be closed
double MainsInitTimeCountdown{ 0.0 }; // current state of main circuit initialization, remaining time (in seconds) until it's ready
int MainCtrlPos = 0; /*polozenie glownego nastawnika*/
int ScndCtrlPos = 0; /*polozenie dodatkowego nastawnika*/
int LightsPos = 0;
@@ -1305,6 +1330,7 @@ public:
double dizel_engagedeltaomega = 0.0; /*roznica predkosci katowych tarcz sprzegla*/
double dizel_n_old = 0.0; /*poredkosc na potrzeby obliczen sprzegiel*/
double dizel_Torque = 0.0; /*poredkosc na potrzeby obliczen sprzegiel*/
double dizel_nreg_min = 0.0; /*predkosc regulatora minimalna, zmienna w hydro*/
/* - zmienne dla przetowrnika momentu */
double hydro_TC_Fill = 0.0; /*napelnienie*/
@@ -1385,6 +1411,7 @@ public:
double fBrakeCtrlPos = -2.0; // płynna nastawa hamulca zespolonego
bool bPantKurek3 = true; // kurek trójdrogowy (pantografu): true=połączenie z ZG, false=połączenie z małą sprężarką // domyślnie zbiornik pantografu połączony jest ze zbiornikiem głównym
bool PantAutoValve { false }; // type of installed pantograph compressor valve
int iProblem = 0; // flagi problemów z taborem, aby AI nie musiało porównywać; 0=może jechać
int iLights[2]; // bity zapalonych świateł tutaj, żeby dało się liczyć pobór prądu
@@ -1517,7 +1544,9 @@ public:
bool CompressorSwitch( bool State, range_t const Notify = range_t::consist );/*! wl/wyl sprezarki*/
/*-funkcje typowe dla lokomotywy elektrycznej*/
void ConverterCheck( double const Timestep ); // przetwornica
void MainsCheck( double const Deltatime );
void PowerCouplersCheck( double const Deltatime );
void ConverterCheck( double const Timestep ); // przetwornica
void HeatingCheck( double const Timestep );
void WaterPumpCheck( double const Timestep );
void WaterHeaterCheck( double const Timestep );
@@ -1557,8 +1586,9 @@ public:
bool dizel_EngageSwitch(double state);
bool dizel_EngageChange(double dt);
bool dizel_AutoGearCheck(void);
double dizel_fillcheck(int mcp);
double dizel_fillcheck(int mcp, double dt);
double dizel_Momentum(double dizel_fill, double n, double dt);
double dizel_MomentumRetarder(double n, double dt); // moment hamowania retardera
void dizel_HeatSet( float const Value );
void dizel_Heat( double const dt );
bool dizel_StartupCheck();

View File

@@ -781,7 +781,7 @@ void TMoverParameters::UpdatePantVolume(double dt)
// Ra 2014-07: kurek trójdrogowy łączy spr.pom. z pantografami i wyłącznikiem ciśnieniowym WS
// Ra 2014-07: zbiornika rozrządu nie pompuje się tu, tylko pantografy; potem można zamknąć
// WS i odpalić resztę
if ((TrainType == dt_EZT) ?
if (PantAutoValve ?
(PantPress < ScndPipePress) :
bPantKurek3) // kurek zamyka połączenie z ZG
{ // zbiornik pantografu połączony ze zbiornikiem głównym - małą sprężarką się tego nie napompuje
@@ -1181,85 +1181,6 @@ double TMoverParameters::ComputeMovement(double dt, double dt1, const TTrackShap
const double Vepsilon = 1e-5;
const double Aepsilon = 1e-3; // ASBSpeed=0.8;
// T_MoverParameters::ComputeMovement(dt, dt1, Shape, Track, ElectricTraction, NewLoc, NewRot);
// // najpierw kawalek z funkcji w pliku mover.pas
TotalCurrent = 0;
double hvc =
std::max(
std::max(
PantFrontVolt,
PantRearVolt ),
ElectricTraction.TractionVoltage * 0.9 );
for( int side = 0; side < 2; ++side ) {
// przekazywanie napiec
auto const oppositeside { ( side == end::front ? end::rear : end::front ) };
auto const liveconnection{
( Couplers[ side ].CouplingFlag & ctrain_power )
|| ( ( Couplers[ side ].CouplingFlag & ctrain_heating )
&& ( Couplers[ side ].Connected->Heating ) ) };
if( liveconnection ) {
auto const &connectedcoupler = Couplers[ side ].Connected->Couplers[ Couplers[ side ].ConnectedNr ];
Couplers[ oppositeside ].power_high.voltage =
std::max(
std::abs( hvc ),
connectedcoupler.power_high.voltage - Couplers[ side ].power_high.current * 0.02 );
}
else {
Couplers[ oppositeside ].power_high.voltage = std::abs( hvc ) - Couplers[ side ].power_high.current * 0.02;
}
}
hvc = Couplers[ end::front ].power_high.voltage + Couplers[ end::rear ].power_high.voltage;
if( std::abs( PantFrontVolt ) + std::abs( PantRearVolt ) < 1.0 ) {
// bez napiecia...
if( hvc != 0.0 ) {
// ...ale jest cos na sprzegach:
// przekazywanie pradow
for( int side = 0; side < 2; ++side ) {
Couplers[ side ].power_high.local = false; // power, if any, will be from external source
if( ( Couplers[ side ].CouplingFlag & ctrain_power )
|| ( ( Couplers[ side ].CouplingFlag & ctrain_heating )
&& ( Couplers[ side ].Connected->Heating ) ) ) {
auto const &connectedcoupler =
Couplers[ side ].Connected->Couplers[
( Couplers[ side ].ConnectedNr == end::front ?
end::rear :
end::front ) ];
Couplers[ side ].power_high.current =
connectedcoupler.power_high.current
+ Itot * Couplers[ side ].power_high.voltage / hvc; // obciążenie rozkladane stosownie do napiec
}
else {
Couplers[ side ].power_high.current = Itot * Couplers[ side ].power_high.voltage / hvc;
}
}
}
}
else
{
for( int side = 0; side < 2; ++side ) {
Couplers[ side ].power_high.local = true; // power is coming from local pantographs
if( ( Couplers[ side ].CouplingFlag & ctrain_power )
|| ( ( Couplers[ side ].CouplingFlag & ctrain_heating )
&& ( Couplers[ side ].Connected->Heating ) ) ) {
auto const &connectedcoupler =
Couplers[ side ].Connected->Couplers[
( Couplers[ side ].ConnectedNr == end::front ?
end::rear :
end::front ) ];
TotalCurrent += connectedcoupler.power_high.current;
Couplers[ side ].power_high.current = 0.0;
}
}
}
if (!TestFlag(DamageFlag, dtrain_out))
{ // Ra: to przepisywanie tu jest bez sensu
RunningShape = Shape;
@@ -1499,10 +1420,11 @@ void TMoverParameters::compute_movement_( double const Deltatime ) {
RunInternalCommand();
// automatyczny rozruch
if (EngineType == TEngineType::ElectricSeriesMotor)
if (AutoRelayCheck())
SetFlag(SoundFlag, sound::relay);
if( EngineType == TEngineType::ElectricSeriesMotor ) {
if( AutoRelayCheck() ) {
SetFlag( SoundFlag, sound::relay );
}
}
if( ( EngineType == TEngineType::DieselEngine )
|| ( EngineType == TEngineType::DieselElectric ) ) {
@@ -1510,6 +1432,12 @@ void TMoverParameters::compute_movement_( double const Deltatime ) {
SetFlag( SoundFlag, sound::relay );
}
}
// TODO: gather and move current calculations to dedicated method
TotalCurrent = 0;
// main circuit
MainsCheck( Deltatime );
// traction motors
MotorBlowersCheck( Deltatime );
// uklady hamulcowe:
@@ -1547,6 +1475,145 @@ void TMoverParameters::compute_movement_( double const Deltatime ) {
BrakeSlippingTimer += Deltatime;
// automatic doors
update_doors( Deltatime );
PowerCouplersCheck( Deltatime );
}
void TMoverParameters::MainsCheck( double const Deltatime ) {
// TODO: move other main circuit checks here
if( MainsInitTime == 0.0 ) { return; }
if( MainsInitTimeCountdown > 0.0 ) {
MainsInitTimeCountdown -= Deltatime;
}
// TBD, TODO: move voltage calculation to separate method and use also in power coupler state calculation?
auto localvoltage { 0.0 };
switch( EnginePowerSource.SourceType ) {
case TPowerSource::CurrentCollector: {
localvoltage =
std::max(
localvoltage,
std::max(
PantFrontVolt,
PantRearVolt ) );
break;
}
default: {
break;
}
}
if( ( localvoltage == 0.0 )
&& ( GetTrainsetVoltage() == 0 ) ) {
MainsInitTimeCountdown = MainsInitTime;
}
}
void TMoverParameters::PowerCouplersCheck( double const Deltatime ) {
// TODO: add support for other power sources
auto localvoltage { 0.0 };
// heating power sources
if( Heating ) {
switch( HeatingPowerSource.SourceType ) {
case TPowerSource::Generator: {
localvoltage = HeatingPowerSource.EngineGenerator.voltage - TotalCurrent * 0.02;
break;
}
case TPowerSource::Main: {
localvoltage = ( true == Mains ? Voltage : 0.0 );
break;
}
default: {
break;
}
}
}
// high voltage power sources
switch( EnginePowerSource.SourceType ) {
case TPowerSource::CurrentCollector: {
localvoltage =
std::max(
localvoltage,
std::max(
PantFrontVolt,
PantRearVolt ) );
break;
}
default: {
break;
}
}
auto const abslocalvoltage { std::abs( localvoltage ) };
auto const localpowersource { ( abslocalvoltage > 1.0 ) };
/*
auto const localpowersource { ( std::abs( PantFrontVolt ) + std::abs( PantRearVolt ) > 1.0 ) };
auto hvc = std::max( PantFrontVolt, PantRearVolt );
*/
// przekazywanie napiec
for( auto side = 0; side < 2; ++side ) {
auto &coupler { Couplers[ side ] };
// NOTE: in the loop we actually update the state of the coupler on the opposite end of the vehicle
auto &oppositecoupler { Couplers[ ( side == end::front ? end::rear : end::front ) ] };
auto const oppositehighvoltagecoupling { ( oppositecoupler.CouplingFlag & coupling::highvoltage ) != 0 };
auto const oppositeheatingcoupling { ( oppositecoupler.CouplingFlag & coupling::heating ) != 0 };
// start with base voltage
oppositecoupler.power_high.voltage = abslocalvoltage;
oppositecoupler.power_high.is_live = false;
oppositecoupler.power_high.is_local = localpowersource; // indicate power source
// draw from external source
if( coupler.Connected != nullptr ) {
auto const &connectedcoupler { coupler.Connected->Couplers[ coupler.ConnectedNr ] };
auto const connectedvoltage { (
connectedcoupler.power_high.is_live ?
connectedcoupler.power_high.voltage :
0.0 ) };
oppositecoupler.power_high.voltage = std::max(
oppositecoupler.power_high.voltage,
connectedvoltage - coupler.power_high.current * 0.02 );
oppositecoupler.power_high.is_live =
( connectedvoltage > 0.1 )
&& ( oppositehighvoltagecoupling || oppositeheatingcoupling );
}
// draw from local source
if( localpowersource ) {
oppositecoupler.power_high.voltage = std::max(
oppositecoupler.power_high.voltage,
abslocalvoltage - coupler.power_high.current * 0.02 );
oppositecoupler.power_high.is_live |=
( abslocalvoltage > 0.1 )
&& ( oppositehighvoltagecoupling || ( oppositeheatingcoupling && localpowersource && Heating ) );
}
}
// przekazywanie pradow
auto couplervoltage { Couplers[ end::front ].power_high.voltage + Couplers[ end::rear ].power_high.voltage };
for( auto side = 0; side < 2; ++side ) {
auto &coupler { Couplers[ side ] };
auto const &connectedothercoupler { coupler.Connected->Couplers[ ( coupler.ConnectedNr == end::front ? end::rear : end::front ) ] };
coupler.power_high.current = 0.0;
if( false == localpowersource ) {
// bez napiecia...
if( couplervoltage != 0.0 ) {
// ...ale jest cos na sprzegach:
coupler.power_high.current = ( Itot + TotalCurrent ) * coupler.power_high.voltage / couplervoltage; // obciążenie rozkladane stosownie do napiec
if( true == coupler.power_high.is_live ) {
coupler.power_high.current += connectedothercoupler.power_high.current;
}
}
}
else {
if( true == coupler.power_high.is_live ) {
TotalCurrent += connectedothercoupler.power_high.current;
}
}
}
}
double TMoverParameters::ShowEngineRotation(int VehN)
@@ -1603,12 +1670,70 @@ void TMoverParameters::ConverterCheck( double const Timestep ) {
// heating system status check
void TMoverParameters::HeatingCheck( double const Timestep ) {
Heating = (
( true == HeatingAllow )
// powered vehicles are generally required to activate their power source to provide heating
// passive vehicles get a pass in this regard
&& ( ( Power < 0.1 )
|| ( true == Mains ) ) );
// update heating devices
// TBD, TODO: move this to a separate method?
switch( HeatingPowerSource.SourceType ) {
case TPowerSource::Generator: {
if( ( HeatingPowerSource.EngineGenerator.engine_revolutions != nullptr )
&& ( HeatingPowerSource.EngineGenerator.revolutions_max > 0 ) ) {
auto &generator { HeatingPowerSource.EngineGenerator };
// TBD, TODO: engine-generator transmission
generator.revolutions = *(generator.engine_revolutions);
auto const absrevolutions { std::abs( generator.revolutions ) };
generator.voltage = (
absrevolutions < generator.revolutions_min ? generator.voltage_min * absrevolutions / generator.revolutions_min :
// absrevolutions > generator.revolutions_max ? generator.voltage_max * absrevolutions / generator.revolutions_max :
interpolate(
generator.voltage_min, generator.voltage_max,
clamp(
( absrevolutions - generator.revolutions_min ) / ( generator.revolutions_max - generator.revolutions_min ),
0.0, 1.0 ) ) )
* sign( generator.revolutions );
}
break;
}
default: {
break;
}
}
// quick check first to avoid unnecessary calls...
if( false == HeatingAllow ) {
Heating = false;
return;
}
// ...detailed check if we're still here
auto const heatingpowerthreshold { 0.1 };
// start with external power sources
auto voltage { 0.0 };
// then try internal ones
switch( HeatingPowerSource.SourceType ) {
case TPowerSource::Generator: {
voltage = HeatingPowerSource.EngineGenerator.voltage;
break;
}
case TPowerSource::PowerCable: {
if( HeatingPowerSource.PowerType == TPowerType::ElectricPower ) {
voltage = GetTrainsetVoltage();
}
break;
}
case TPowerSource::Main: {
voltage = ( true == Mains ? Voltage : 0.0 );
break;
}
default: {
break;
}
}
Heating = ( std::abs( voltage ) > heatingpowerthreshold );
if( Heating ) {
TotalCurrent += 1000 * HeatingPower / voltage; // heater power cost presumably specified in kilowatts
}
}
// water pump status check
@@ -2756,6 +2881,7 @@ void TMoverParameters::MainSwitch_( bool const State ) {
if( ( false == State )
|| ( ( ( ScndCtrlPos == 0 ) || ( EngineType == TEngineType::ElectricInductionMotor ) )
&& ( ( ConvOvldFlag == false ) || ( TrainType == dt_EZT ) )
&& ( MainsInitTimeCountdown <= 0.0 )
&& ( true == NoVoltRelay )
&& ( true == OvervoltageRelay )
&& ( LastSwitchingTime > CtrlDelay )
@@ -3874,7 +4000,6 @@ void TMoverParameters::ComputeConstans(void)
double BearingF, RollF, HideModifier;
double Curvature; // Ra 2014-07: odwrotność promienia
TotalCurrent = 0; // Ra 2014-04: tu zerowanie, aby EZT mogło pobierać prąd innemu członowi
TotalMass = ComputeMass();
TotalMassxg = TotalMass * g; // TotalMass*g
BearingF = 2.0 * (DamageFlag && dtrain_bearing);
@@ -4013,9 +4138,10 @@ void TMoverParameters::ComputeTotalForce(double dt) {
else
Voltage = RunningTraction.TractionVoltage * DirAbsolute; // ActiveDir*CabNo;
} // bo nie dzialalo
// TODO: clean up this elseif to match changes in power coupling code
else if( ( EngineType == TEngineType::ElectricInductionMotor )
|| ( ( ( Couplers[ end::front ].CouplingFlag & ctrain_power ) == ctrain_power )
|| ( ( Couplers[ end::rear ].CouplingFlag & ctrain_power ) == ctrain_power ) ) ) {
|| ( ( Couplers[ end::rear ].CouplingFlag & ctrain_power ) == ctrain_power ) ) ) {
// potem ulepszyc! pantogtrafy!
Voltage =
std::max(
@@ -4405,7 +4531,7 @@ double TMoverParameters::TractionForce( double dt ) {
tmp = DElist[ MainCtrlPos ].RPM / 60.0;
if( ( true == Heating )
if( ( true == HeatingAllow )
&& ( HeatingPower > 0 )
&& ( EngineHeatingRPM > 0 ) ) {
// bump engine revolutions up if needed, when heating is on
@@ -4559,7 +4685,7 @@ double TMoverParameters::TractionForce( double dt ) {
EnginePower = ( 2 * dizel_Mstand + dmoment ) * enrot * ( 2.0 * M_PI / 1000.0 );
if( MainCtrlPowerPos() > 1 ) {
// dodatkowe opory z powodu sprezarki}
dmoment -= dizel_Mstand * ( 0.2 * enrot / dizel_nmax );
// dmoment -= dizel_Mstand * ( 0.2 * enrot / dizel_nmax ); //yB: skąd to w ogóle się bierze?!
}
break;
}
@@ -4742,6 +4868,8 @@ double TMoverParameters::TractionForce( double dt ) {
{
Mm = dmoment; //bylo * dizel_engage
Mw = Mm * dtrans; // dmoment i dtrans policzone przy okazji enginerotation
if ((hydro_R) && (hydro_R_Placement == 0))
Mw -= dizel_MomentumRetarder(nrot * Transmision.Ratio, dt) * Transmision.Ratio;
Fw = Mw * 2.0 / WheelDiameter / NPoweredAxles;
Ft = Fw * NPoweredAxles; // sila trakcyjna
Ft = Ft * DirAbsolute; // ActiveDir*CabNo;
@@ -5282,12 +5410,12 @@ double TMoverParameters::TractionForce( double dt ) {
Im = eimv[eimv_If];
if ((eimv[eimv_Ipoj] >= 0))
Vadd *= (1.0 - 2.0 * dt);
else if ((Voltage < EnginePowerSource.CollectorParameters.MaxV))
else if ((std::abs(Voltage) < EnginePowerSource.CollectorParameters.MaxV))
Vadd *= (1.0 - dt);
else
Vadd = Max0R(
Vadd * (1.0 - 0.2 * dt),
0.007 * (Voltage - (EnginePowerSource.CollectorParameters.MaxV - 100)));
0.007 * (std::abs(Voltage) - (EnginePowerSource.CollectorParameters.MaxV - 100)));
Itot = eimv[eimv_Ipoj] * (0.01 + Min0R(0.99, 0.99 - Vadd));
EnginePower = abs(eimv[eimv_Ic] * eimv[eimv_U] * NPoweredAxles) / 1000;
@@ -6106,9 +6234,34 @@ void TMoverParameters::CheckEIMIC(double dt)
}
break;
case 3:
eimic -= clamp(-UniCtrlList[MainCtrlPos].SetCtrlVal + eimic, 0.0, dt * UniCtrlList[MainCtrlPos].SpeedDown); //odejmuj do X
eimic += clamp(UniCtrlList[MainCtrlPos].SetCtrlVal - eimic, 0.0, dt * UniCtrlList[MainCtrlPos].SpeedUp); //dodawaj do X
eimic = clamp(eimic, UniCtrlList[MainCtrlPos].MinCtrlVal, UniCtrlList[MainCtrlPos].MaxCtrlVal);
if ((UniCtrlList[MainCtrlPos].mode != BrakeCtrlPos) && (MainCtrlActualPos == MainCtrlPos)) //there was no move of controller, but brake only
{
if (BrakeCtrlPos < UniCtrlList[MainCtrlPosNo].mode)
BrakeLevelSet(UniCtrlList[MainCtrlPosNo].mode); //bottom clamping
if (BrakeCtrlPos > UniCtrlList[0].mode)
BrakeLevelSet(UniCtrlList[0].mode); //top clamping
while (BrakeCtrlPos > UniCtrlList[MainCtrlPos].mode) DecMainCtrl(1); //find nearest position
while (BrakeCtrlPos < UniCtrlList[MainCtrlPos].mode) IncMainCtrl(1); //find nearest position
}
else //controller was moved
BrakeLevelSet(UniCtrlList[MainCtrlPos].mode);
if ((MainCtrlActualPos != MainCtrlPos) || (LastRelayTime>InitialCtrlDelay))
{
eimic -= clamp(-UniCtrlList[MainCtrlPos].SetCtrlVal + eimic, 0.0, (MainCtrlActualPos == MainCtrlPos ? dt * UniCtrlList[MainCtrlPos].SpeedDown : sign(UniCtrlList[MainCtrlPos].SpeedDown) * 0.01)); //odejmuj do X
eimic += clamp(UniCtrlList[MainCtrlPos].SetCtrlVal - eimic, 0.0, (MainCtrlActualPos == MainCtrlPos ? dt * UniCtrlList[MainCtrlPos].SpeedUp : sign(UniCtrlList[MainCtrlPos].SpeedUp) * 0.01)); //dodawaj do X
eimic = clamp(eimic, UniCtrlList[MainCtrlPos].MinCtrlVal, UniCtrlList[MainCtrlPos].MaxCtrlVal);
}
if (MainCtrlActualPos == MainCtrlPos)
LastRelayTime += dt;
else
{
LastRelayTime = 0;
MainCtrlActualPos = MainCtrlPos;
}
if (Hamulec->GetEDBCP() > 0.3 && eimic < 0) //when braking with pneumatic brake
eimic = 0; //shut off retarder
}
auto const eimicpowerenabled {
( ( true == Mains ) || ( Power == 0.0 ) )
@@ -6231,6 +6384,22 @@ bool TMoverParameters::dizel_AutoGearCheck(void)
if (Mains)
{
if (EIMCtrlType > 0) //sterowanie komputerowe
{
if (dizel_automaticgearstatus == 0)
{
if ((hydro_TC && hydro_TC_Fill > 0.01) || (eimic_real > 0.0))
dizel_EngageSwitch(1.0);
else
if (Vel > hydro_R_EngageVel && hydro_R && hydro_R_Fill > 0.01)
dizel_EngageSwitch(0.5);
else
dizel_EngageSwitch(0.0);
}
else
dizel_EngageSwitch(0.0);
}
else
if (dizel_automaticgearstatus == 0) // ustaw cisnienie w silowniku sprzegla}
switch (RList[MainCtrlPos].Mn)
{
@@ -6362,7 +6531,7 @@ bool TMoverParameters::dizel_Update(double dt) {
dizel_EngageChange( dt );
DU = dizel_AutoGearCheck();
double const fillspeed { 2 };
dizel_fill = dizel_fill + fillspeed * dt * ( dizel_fillcheck( MainCtrlPos ) - dizel_fill );
dizel_fill = dizel_fill + fillspeed * dt * ( dizel_fillcheck( MainCtrlPos , dt ) - dizel_fill );
}
dizel_Heat( dt );
@@ -6374,7 +6543,7 @@ bool TMoverParameters::dizel_Update(double dt) {
// Q: 20160715
// oblicza napelnienie, uzwglednia regulator obrotow
// *************************************************************************************************
double TMoverParameters::dizel_fillcheck(int mcp)
double TMoverParameters::dizel_fillcheck(int mcp, double dt)
{
auto realfill { 0.0 };
@@ -6391,11 +6560,30 @@ double TMoverParameters::dizel_fillcheck(int mcp)
}
else {
// napelnienie zalezne od MainCtrlPos
realfill = RList[ mcp ].R;
if (EIMCtrlType > 0)
{
realfill = std::max(0.0, eimic_real);
if (eimic_real>0 && !hydro_TC_Lockup)
{
dizel_nreg_min = std::min(dizel_nreg_min + 2.5 * dt, dizel_nmin_hdrive + eimic_real * dizel_nmin_hdrive_factor);
}
else
{
if (Vel < hydro_R_EngageVel && hydro_R && hydro_R_Fill > 0.01)
dizel_nreg_min = std::min(dizel_nreg_min + 5.0 * dt, dizel_nmin_retarder);
else
dizel_nreg_min = dizel_nmin;
}
}
else
realfill = RList[mcp].R;
}
if (dizel_nmax_cutoff > 0)
{
auto nreg { 0.0 };
if (EIMCtrlType > 0)
nreg = (eimic_real > 0 ? dizel_nmax : dizel_nmin);
else
switch (RList[MainCtrlPos].Mn)
{
case 0:
@@ -6434,7 +6622,7 @@ double TMoverParameters::dizel_fillcheck(int mcp)
realfill = 0;
if (enrot < nreg) //pod predkoscia regulatora dawka zadana
realfill = realfill;
if ((enrot < dizel_nmin * 0.98)&&(RList[mcp].R>0.001)) //jesli ponizej biegu jalowego i niezerowa dawka, to dawaj pelna
if ((enrot < dizel_nreg_min)&&(RList[mcp].R>0.001)) //jesli ponizej biegu jalowego i niezerowa dawka, to dawaj pelna
realfill = 1;
}
}
@@ -6460,6 +6648,8 @@ double TMoverParameters::dizel_Momentum(double dizel_fill, double n, double dt)
if( enrot > 0 ) {
Moment = ( dizel_Mmax - ( dizel_Mmax - dizel_Mnmax ) * square( ( enrot - dizel_nMmax ) / ( dizel_nMmax - dizel_nmax ) ) ) * dizel_fill - dizel_Mstand;
if ((hydro_R) && (hydro_R_Placement == 2))
Moment -= dizel_MomentumRetarder(enrot, dt);
}
else {
Moment = -dizel_Mstand;
@@ -6477,10 +6667,11 @@ double TMoverParameters::dizel_Momentum(double dizel_fill, double n, double dt)
if (hydro_TC) //jesli przetwornik momentu
{
//napelnianie przetwornika
if ((MainCtrlPowerPos() > 0) && (Mains) && (enrot>dizel_nmin*0.9))
bool IsPower = (EIMCtrlType > 0 ? eimic_real > 0 : MainCtrlPowerPos() > 0);
if ((IsPower) && (Mains) && (enrot>dizel_nmin*0.9))
hydro_TC_Fill += hydro_TC_FillRateInc * dt;
//oproznianie przetwornika
if (((IsMainCtrlNoPowerPos()) && (Vel<3))
if (((!IsPower) && (Vel<dizel_maxVelANS))
|| (!Mains)
|| (enrot<dizel_nmin*0.8))
hydro_TC_Fill -= hydro_TC_FillRateDec * dt;
@@ -6488,10 +6679,10 @@ double TMoverParameters::dizel_Momentum(double dizel_fill, double n, double dt)
hydro_TC_Fill = clamp(hydro_TC_Fill, 0.0, 1.0);
//blokowanie sprzegla blokującego
if ((Vel > hydro_TC_LockupSpeed) && (Mains) && (enrot > 0.9 * dizel_nmin) && (MainCtrlPowerPos() > 0))
if ((Vel > hydro_TC_LockupSpeed) && (Mains) && (enrot > 0.9 * dizel_nmin) && (IsPower))
hydro_TC_LockupRate += hydro_TC_FillRateInc*dt;
//luzowanie sprzegla blokujacego
if ((Vel < (MainCtrlPowerPos() > 0 ? hydro_TC_LockupSpeed : hydro_TC_UnlockSpeed)) || (!Mains) || (enrot < 0.8 * dizel_nmin))
if ((Vel < (IsPower ? hydro_TC_LockupSpeed : hydro_TC_UnlockSpeed)) || (!Mains) || (enrot < 0.8 * dizel_nmin))
hydro_TC_LockupRate -= hydro_TC_FillRateDec*dt;
//obcinanie zakresu
hydro_TC_LockupRate = clamp(hydro_TC_LockupRate, 0.0, 1.0);
@@ -6578,6 +6769,8 @@ double TMoverParameters::dizel_Momentum(double dizel_fill, double n, double dt)
double enrot_max = enrot + (Min0R(TorqueC, TorqueL + abs(hydro_TC_TorqueIn)) + Moment) / dizel_AIM * dt;
enrot = clamp(n,enrot_min,enrot_max);
}
if ((hydro_R) && (hydro_R_Placement == 1))
gearMoment -= dizel_MomentumRetarder(hydro_TC_nOut, dt);
if( ( enrot <= 0 ) && ( false == dizel_spinup ) ) {
@@ -6590,6 +6783,40 @@ double TMoverParameters::dizel_Momentum(double dizel_fill, double n, double dt)
return gearMoment;
}
double TMoverParameters::dizel_MomentumRetarder(double n, double dt)
{
double RetarderRequest = (Mains ? std::max(0.0, -eimic_real) : 0);
if (Vel < hydro_R_MinVel)
RetarderRequest = 0;
if ((hydro_R_Placement == 2) && (enrot < dizel_nmin))
{
RetarderRequest = 0;
}
hydro_R_n = n * 60;
if (hydro_R_Fill < RetarderRequest) //gdy zadane hamowanie
{
hydro_R_Fill = std::min(hydro_R_Fill + hydro_R_FillRateInc*dt, RetarderRequest);
}
else
{
hydro_R_Fill = std::max(hydro_R_Fill - hydro_R_FillRateDec*dt, RetarderRequest);
}
double Moment = hydro_R_MaxTorque;
double pwr = Moment * n * M_PI * 2 * 0.001;
if (pwr > hydro_R_MaxPower)
Moment = Moment * hydro_R_MaxPower / pwr;
double moment_in = n*n*hydro_R_TorqueInIn;
Moment = std::min(moment_in, Moment * hydro_R_Fill);
hydro_R_Torque = Moment;
return Moment;
}
// sets component temperatures to specified value
void TMoverParameters::dizel_HeatSet( float const Value ) {
@@ -6899,8 +7126,8 @@ bool TMoverParameters::ChangeDoorPermitPreset( int const Change, range_t const N
Doors.permit_preset = clamp<int>( Doors.permit_preset + Change, 0, Doors.permit_presets.size() - 1 );
auto const doors { Doors.permit_presets[ Doors.permit_preset ] };
auto const permitleft = doors & 1;
auto const permitright = doors & 2;
auto const permitleft { ( ( doors & 1 ) != 0 ) };
auto const permitright { ( ( doors & 2 ) != 0 ) };
PermitDoors( ( CabNo > 0 ? side::left : side::right ), permitleft, Notify );
PermitDoors( ( CabNo > 0 ? side::right : side::left ), permitright, Notify );
@@ -7358,15 +7585,11 @@ double TMoverParameters::GetTrainsetVoltage(void)
{//ABu: funkcja zwracajaca napiecie dla calego skladu, przydatna dla EZT
return std::max(
( ( ( Couplers[end::front].Connected )
&& ( ( Couplers[ end::front ].CouplingFlag & ctrain_power )
|| ( ( Couplers[ end::front ].CouplingFlag & ctrain_heating )
&& ( Couplers[ end::front ].Connected->Heating ) ) ) ) ?
&& ( Couplers[ end::front ].Connected->Couplers[ Couplers[ end::front ].ConnectedNr ].power_high.is_live ) ) ?
Couplers[end::front].Connected->Couplers[ Couplers[end::front].ConnectedNr ].power_high.voltage :
0.0 ),
( ( ( Couplers[end::rear].Connected )
&& ( ( Couplers[ end::rear ].CouplingFlag & ctrain_power )
|| ( ( Couplers[ end::rear ].CouplingFlag & ctrain_heating )
&& ( Couplers[ end::rear ].Connected->Heating ) ) ) ) ?
&& ( Couplers[ end::rear ].Connected->Couplers[ Couplers[ end::rear ].ConnectedNr ].power_high.is_live ) ) ?
Couplers[ end::rear ].Connected->Couplers[ Couplers[ end::rear ].ConnectedNr ].power_high.voltage :
0.0 ) );
}
@@ -7808,13 +8031,19 @@ bool TMoverParameters::LoadFIZ(std::string chkpath)
std::string file = chkpath + TypeName + ".fiz";
WriteLog("LOAD FIZ FROM " + file);
/*
std::ifstream in(file);
if (!in.is_open())
{
WriteLog("E8 - FIZ FILE NOT EXIST.");
return false;
}
*/
cParser fizparser( file, cParser::buffer_FILE );
if( false == fizparser.ok() ) {
WriteLog( "E8 - FIZ FILE NOT EXIST." );
return false;
}
ConversionError = 0;
@@ -7822,8 +8051,13 @@ bool TMoverParameters::LoadFIZ(std::string chkpath)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
std::unordered_map<std::string, std::string> fizlines;
std::string inputline;
/*
while (std::getline(in, inputline))
{
*/
while( fizparser.ok() ) {
inputline = fizparser.getToken<std::string>( false, "\n" );
bool comment = ( ( inputline.find('#') != std::string::npos )
|| ( inputline.compare( 0, 2, "//" ) == 0 ) );
if( true == comment ) {
@@ -7857,7 +8091,7 @@ bool TMoverParameters::LoadFIZ(std::string chkpath)
startRLIST = false;
continue;
}
if (issection("END-RL", inputline)) {
if (issection("END-UCL", inputline)) {
startBPT = false;
startUCLIST = false;
continue;
@@ -8070,7 +8304,7 @@ bool TMoverParameters::LoadFIZ(std::string chkpath)
startBPT = false;
fizlines.emplace("UCList", inputline);
startUCLIST = true; LISTLINE = 0;
LoadFIZ_RList(inputline);
LoadFIZ_UCList(inputline);
continue;
}
@@ -8124,7 +8358,7 @@ bool TMoverParameters::LoadFIZ(std::string chkpath)
continue;
}
if (true == startUCLIST) {
readRList(inputline);
readUCList(inputline);
continue;
}
if( true == startDLIST ) {
@@ -8144,8 +8378,9 @@ bool TMoverParameters::LoadFIZ(std::string chkpath)
continue;
}
} // while line
/*
in.close();
*/
// Operacje na zebranych parametrach - przypisywanie do wlasciwych zmiennych i ustawianie
// zaleznosci
@@ -8756,6 +8991,8 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) {
false;
extract_value(SpeedCtrlAutoTurnOffFlag, "SpeedCtrlATOF", line, "");
// main circuit
extract_value( MainsInitTime, "MainInitTime", line, "" );
// converter
{
std::map<std::string, start_t> starts {
@@ -8793,6 +9030,9 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) {
lookup->second :
start_t::manual;
}
// pantograph compressor valve
PantAutoValve = ( TrainType == dt_EZT ); // legacy code behaviour, automatic valve was initially installed in all EMUs
extract_value( PantAutoValve, "PantAutoValve", line, "" );
// fuel pump
{
auto lookup = starts.find( extract_value( "FuelStart", line ) );
@@ -8831,9 +9071,9 @@ void TMoverParameters::LoadFIZ_Cntrl( std::string const &line ) {
void TMoverParameters::LoadFIZ_Blending(std::string const &line) {
extract_value(MED_Vmax, "MED_Vmax", line, to_string(Vmax));
extract_value(MED_Vmin, "MED_Vmin", line, "0");
extract_value(MED_Vmin, "MED_Vmin", line, "");
extract_value(MED_Vref, "MED_Vref", line, to_string(Vmax));
extract_value(MED_amax, "MED_amax", line, "9.81");
extract_value(MED_amax, "MED_amax", line, "");
extract_value(MED_EPVC, "MED_EPVC", line, "");
extract_value(MED_Ncor, "MED_Ncor", line, "");
@@ -8841,10 +9081,10 @@ void TMoverParameters::LoadFIZ_Blending(std::string const &line) {
void TMoverParameters::LoadFIZ_DCEMUED(std::string const &line) {
extract_value(DCEMUED_CC, "CouplerCheck", line, "0");
extract_value(DCEMUED_EP_max_Vel, "EP_max_Vel", line, "0");
extract_value(DCEMUED_EP_min_Im, "EP_min_Im", line, "0");
extract_value(DCEMUED_EP_delay, "EP_delay", line, "0");
extract_value(DCEMUED_CC, "CouplerCheck", line, "");
extract_value(DCEMUED_EP_max_Vel, "EP_max_Vel", line, "");
extract_value(DCEMUED_EP_min_Im, "EP_min_Im", line, "");
extract_value(DCEMUED_EP_delay, "EP_delay", line, "");
}
@@ -8873,12 +9113,13 @@ void TMoverParameters::LoadFIZ_Power( std::string const &Line ) {
EnginePowerSource.SourceType = LoadFIZ_SourceDecode( extract_value( "EnginePower", Line ) );
LoadFIZ_PowerParamsDecode( EnginePowerSource, "", Line );
/*
if( ( EnginePowerSource.SourceType == TPowerSource::Generator )
&& ( EnginePowerSource.GeneratorEngine == TEngineType::WheelsDriven ) ) {
// perpetuum mobile?
ConversionError = 666;
}
*/
if( Power == 0.0 ) {
//jeśli nie ma mocy, np. rozrządcze EZT
EnginePowerSource.SourceType = TPowerSource::NotDefined;
@@ -8934,6 +9175,19 @@ void TMoverParameters::LoadFIZ_Engine( std::string const &Input ) {
extract_value( dizel_nmin, "nmin", Input, "" );
dizel_nmin /= 60.0;
dizel_nreg_min = dizel_nmin * 0.98;
extract_value(dizel_nmin_hdrive, "nmin_hdrive", Input, "");
dizel_nmin_hdrive /= 60.0;
if (dizel_nmin_hdrive == 0.0) {
dizel_nmin_hdrive = dizel_nmin;
}
extract_value(dizel_nmin_hdrive_factor, "nmin_hdrive_factor", Input, "");
dizel_nmin_hdrive_factor /= 60.0;
extract_value(dizel_nmin_retarder, "nmin_retarder", Input, "");
dizel_nmin_retarder /= 60.0;
if (dizel_nmin_retarder == 0.0) {
dizel_nmin_retarder = dizel_nmin;
}
// TODO: unify naming scheme and sort out which diesel engine params are used where and how
extract_value( nmax, "nmax", Input, "" );
nmax /= 60.0;
@@ -8968,6 +9222,7 @@ void TMoverParameters::LoadFIZ_Engine( std::string const &Input ) {
extract_value(hydro_TC_TorqueOutOut, "TC_TOO", Input, "");
extract_value(hydro_TC_LockupSpeed, "TC_LS", Input, "");
extract_value(hydro_TC_UnlockSpeed, "TC_ULS", Input, "");
extract_value(dizel_maxVelANS, "MaxVelANS", Input, "");
extract_value(hydro_R, "IsRetarder", Input, "");
if (true == hydro_R) {
@@ -8978,6 +9233,7 @@ void TMoverParameters::LoadFIZ_Engine( std::string const &Input ) {
extract_value(hydro_R_FillRateInc, "R_FRI", Input, "");
extract_value(hydro_R_FillRateDec, "R_FRD", Input, "");
extract_value(hydro_R_MinVel, "R_MinVel", Input, "");
extract_value(hydro_R_EngageVel, "R_EngageVel", Input, "");
}
}
break;
@@ -9201,12 +9457,31 @@ void TMoverParameters::LoadFIZ_PowerParamsDecode( TPowerParameters &Powerparamet
}
case TPowerSource::Transducer: {
extract_value( Powerparameters.InputVoltage, Prefix + "TransducerInputV", Line, "" );
extract_value( Powerparameters.Transducer.InputVoltage, Prefix + "TransducerInputV", Line, "" );
break;
}
case TPowerSource::Generator: {
// prime mover for the generator
auto &generatorparameters { Powerparameters.EngineGenerator };
Powerparameters.GeneratorEngine = LoadFIZ_EngineDecode( extract_value( Prefix + "GeneratorEngine", Line ) );
auto const enginetype { LoadFIZ_EngineDecode( extract_value( Prefix + "GeneratorEngine", Line ) ) };
if( enginetype == TEngineType::Main ) {
generatorparameters.engine_revolutions = &enrot;
}
else {
// TODO: for engine types other than Main create requested engine object and link to its revolutions
generatorparameters.engine_revolutions = nullptr;
generatorparameters.revolutions = 0;
generatorparameters.voltage = 0;
}
// config
extract_value( generatorparameters.voltage_min, Prefix + "GeneratorMinVoltage", Line, "0" );
extract_value( generatorparameters.voltage_max, Prefix + "GeneratorMaxVoltage", Line, "0" );
// NOTE: for consistency the fiz file specifies revolutions per minute
extract_value( generatorparameters.revolutions_min, Prefix + "GeneratorMinRPM", Line, "0" );
extract_value( generatorparameters.revolutions_max, Prefix + "GeneratorMaxRPM", Line, "0" );
generatorparameters.revolutions_min /= 60;
generatorparameters.revolutions_max /= 60;
break;
}
case TPowerSource::Accumulator: {
@@ -9293,7 +9568,8 @@ TPowerSource TMoverParameters::LoadFIZ_SourceDecode( std::string const &Source )
{ "CurrentCollector", TPowerSource::CurrentCollector },
{ "PowerCable", TPowerSource::PowerCable },
{ "Heater", TPowerSource::Heater },
{ "Internal", TPowerSource::InternalSource }
{ "Internal", TPowerSource::InternalSource },
{ "Main", TPowerSource::Main }
};
auto lookup = powersources.find( Source );
return
@@ -9312,7 +9588,8 @@ TEngineType TMoverParameters::LoadFIZ_EngineDecode( std::string const &Engine )
{ "Dumb", TEngineType::Dumb },
{ "DieselElectric", TEngineType::DieselElectric },
{ "DumbDE", TEngineType::DieselElectric },
{ "ElectricInductionMotor", TEngineType::ElectricInductionMotor }
{ "ElectricInductionMotor", TEngineType::ElectricInductionMotor },
{ "Main", TEngineType::Main }
};
auto lookup = enginetypes.find( Engine );
return
@@ -9530,6 +9807,17 @@ bool TMoverParameters::CheckLocomotiveParameters(bool ReadyFlag, int Dir)
if( LightsPosNo > 0 )
LightsPos = LightsDefPos;
// NOTE: legacy compatibility behaviour for vehicles without defined heating power source
if( ( EnginePowerSource.SourceType == TPowerSource::CurrentCollector )
&& ( HeatingPowerSource.SourceType == TPowerSource::NotDefined ) ) {
HeatingPowerSource.SourceType = TPowerSource::Main;
}
if( ( HeatingPowerSource.SourceType == TPowerSource::NotDefined )
&& ( HeatingPower > 0 ) ) {
HeatingPowerSource.SourceType = TPowerSource::PowerCable;
HeatingPowerSource.PowerType = TPowerType::ElectricPower;
}
// checking ready flag
// to dac potem do init
if( ReadyFlag ) // gotowy do drogi

View File

@@ -57,7 +57,7 @@ TModelsManager::LoadModel(std::string const &Name, bool dynamic) {
}
TModel3d *
TModelsManager::GetModel(std::string const &Name, bool const Dynamic)
TModelsManager::GetModel(std::string const &Name, bool const Dynamic, bool const Logerrors )
{ // model może być we wpisie "node...model" albo "node...dynamic", a także być dodatkowym w dynamic
// (kabina, wnętrze, ładunek)
// dla "node...dynamic" mamy podaną ścieżkę w "\dynamic\" i musi być co najmniej 1 poziom, zwkle
@@ -106,7 +106,9 @@ TModelsManager::GetModel(std::string const &Name, bool const Dynamic)
}
else {
// there's nothing matching in the databank nor on the disk, report failure...
ErrorLog( "Bad file: failed do locate 3d model file \"" + filename + "\"", logtype::file );
if( Logerrors ) {
ErrorLog( "Bad file: failed do locate 3d model file \"" + filename + "\"", logtype::file );
}
// ...and link it with the error model slot
m_modelsmap.emplace( filename, null_handle );
}

View File

@@ -22,7 +22,7 @@ private:
class TModelsManager {
public:
// McZapkie: dodalem sciezke, notabene Path!=Patch :)
static TModel3d *GetModel( std::string const &Name, bool dynamic = false );
static TModel3d *GetModel( std::string const &Name, bool const dynamic = false, bool const Logerrors = true );
private:
// types:

View File

@@ -86,16 +86,7 @@ Commands will be written in [`Bash`](https://www.gnu.org/software/bash/). No-Lin
- `_d` is debug flag.
If you currently have MaSzyna assets, just copy executable to install directory.
Else you must download and unpack assets.
Links:
- http://eu07.pl/theme/MaSzyna1803.zip
- http://eu07.pl/theme/MaSzynaP1805.zip
- http://eu07.pl/theme/MaSzynaP1806.zip
- http://eu07.pl/theme/MaSzynaP1807.zip
- http://stuff.eu07.pl/MaSzynaP1810.zip
Override every file.
You can download assets from [eu07.pl](https://eu07.pl/).
**Note:** Linux users may have issue, that `Rainsted` (third party starter), may not detect executable. In that case it is possible, to simulate Windows executable, by creation shell script, like this:

View File

@@ -529,7 +529,7 @@ glm::vec3
TTraction::wire_color() const {
glm::vec3 color;
if( !DebugModeFlag || GfxRenderer.settings.force_normal_traction_render )
if( !GfxRenderer.settings.traction_debug )
{
switch( Material ) { // Ra: kolory podzieliłem przez 2, bo po zmianie ambient za jasne były
// trzeba uwzględnić kierunek świecenia Słońca - tylko ze Słońcem widać kolor

View File

@@ -15,6 +15,8 @@ http://mozilla.org/MPL/2.0/.
class TTractionPowerSource : public scene::basic_node {
friend class debug_panel;
public:
// constructor
TTractionPowerSource( scene::node_data const &Nodedata );

View File

@@ -17,8 +17,9 @@ http://mozilla.org/MPL/2.0/.
#include "Globals.h"
#include "simulation.h"
#include "Camera.h"
#include "Event.h"
#include "simulationtime.h"
#include "Camera.h"
#include "Logs.h"
#include "MdlMngr.h"
#include "Model3d.h"
@@ -340,6 +341,7 @@ TTrain::commandhandler_map const TTrain::m_commandhandlers = {
{ user_command::radiochanneldecrease, &TTrain::OnCommand_radiochanneldecrease },
{ user_command::radiostopsend, &TTrain::OnCommand_radiostopsend },
{ user_command::radiostoptest, &TTrain::OnCommand_radiostoptest },
{ user_command::radiocall3send, &TTrain::OnCommand_radiocall3send },
{ user_command::cabchangeforward, &TTrain::OnCommand_cabchangeforward },
{ user_command::cabchangebackward, &TTrain::OnCommand_cabchangebackward },
{ user_command::generictoggle0, &TTrain::OnCommand_generictoggle },
@@ -457,6 +459,8 @@ dictionary_source *TTrain::GetTrainState() {
// basic systems state data
dict->insert( "battery", mvControlled->Battery );
dict->insert( "linebreaker", mvControlled->Mains );
dict->insert( "main_init", ( mvControlled->MainsInitTimeCountdown < mvControlled->MainsInitTime ) && ( mvControlled->MainsInitTimeCountdown > 0.0 ) );
dict->insert( "main_ready", ( false == mvControlled->Mains ) && ( fHVoltage > 0.0 ) && ( mvControlled->MainsInitTimeCountdown <= 0.0 ) );
dict->insert( "converter", mvControlled->ConverterFlag );
dict->insert( "converter_overload", mvControlled->ConvOvldFlag );
dict->insert( "compress", mvControlled->CompressorFlag );
@@ -4713,10 +4717,10 @@ void TTrain::OnCommand_doorcloseall( TTrain *Train, command_data const &Command
if( Command.action == GLFW_PRESS ) {
if( Train->mvOccupied->Doors.has_autowarning ) {
// automatic departure signal delays actual door closing until the button is released
Train->mvOccupied->signal_departure( true );
}
else {
if( Train->ggDoorAllOffButton.type() != TGaugeType::push_delayed ) {
// delays the action until the button is released
Train->mvOccupied->OperateDoors( side::right, false );
Train->mvOccupied->OperateDoors( side::left, false );
}
@@ -4727,10 +4731,10 @@ void TTrain::OnCommand_doorcloseall( TTrain *Train, command_data const &Command
Train->ggDoorAllOffButton.UpdateValue( 1.0, Train->dsbSwitch );
}
else if( Command.action == GLFW_RELEASE ) {
// release the button
if( Train->mvOccupied->Doors.has_autowarning ) {
// automatic departure signal delays actual door closing until the button is released
Train->mvOccupied->signal_departure( false );
}
if( Train->ggDoorAllOffButton.type() == TGaugeType::push_delayed ) {
// now we can actually close the door
Train->mvOccupied->OperateDoors( side::right, false );
Train->mvOccupied->OperateDoors( side::left, false );
@@ -5006,6 +5010,7 @@ void TTrain::OnCommand_radiostoptest( TTrain *Train, command_data const &Command
if( Command.action == GLFW_PRESS ) {
if( ( Train->RadioChannel() == 10 )
&& ( true == Train->mvOccupied->Radio )
&& ( Train->mvControlled->Battery || Train->mvControlled->ConverterFlag ) ) {
Train->Dynamic()->RadioStop();
}
@@ -5018,6 +5023,23 @@ void TTrain::OnCommand_radiostoptest( TTrain *Train, command_data const &Command
}
}
void TTrain::OnCommand_radiocall3send( TTrain *Train, command_data const &Command ) {
if( Command.action == GLFW_PRESS ) {
if( ( Train->RadioChannel() != 10 )
&& ( true == Train->mvOccupied->Radio )
&& ( Train->mvControlled->Battery || Train->mvControlled->ConverterFlag ) ) {
simulation::Events.queue_receivers( radio_message::call3, Train->Dynamic()->GetPosition() );
}
// visual feedback
Train->ggRadioCall3.UpdateValue( 1.0 );
}
else if( Command.action == GLFW_RELEASE ) {
// visual feedback
Train->ggRadioCall3.UpdateValue( 0.0 );
}
}
void TTrain::OnCommand_cabchangeforward( TTrain *Train, command_data const &Command ) {
if( Command.action == GLFW_PRESS ) {
@@ -5032,6 +5054,10 @@ void TTrain::OnCommand_cabchangeforward( TTrain *Train, command_data const &Comm
Train->MoveToVehicle(dynobj);
}
}
// HACK: match consist door permit state with the preset in the active cab
if( Train->ggDoorPermitPresetButton.SubModel != nullptr ) {
Train->mvOccupied->ChangeDoorPermitPreset( 0 );
}
}
}
@@ -5049,6 +5075,10 @@ void TTrain::OnCommand_cabchangebackward( TTrain *Train, command_data const &Com
Train->MoveToVehicle(dynobj);
}
}
// HACK: match consist door permit state with the preset in the active cab
if( Train->ggDoorPermitPresetButton.SubModel != nullptr ) {
Train->mvOccupied->ChangeDoorPermitPreset( 0 );
}
}
}
@@ -5127,10 +5157,11 @@ bool TTrain::Update( double const Deltatime )
if( ( ggMainButton.GetDesiredValue() > 0.95 )
|| ( ggMainOnButton.GetDesiredValue() > 0.95 ) ) {
// keep track of period the line breaker button is held down, to determine when/if circuit closes
if( ( fHVoltage > 0.5 * mvControlled->EnginePowerSource.MaxVoltage )
|| ( ( mvControlled->EngineType != TEngineType::ElectricSeriesMotor )
&& ( mvControlled->EngineType != TEngineType::ElectricInductionMotor )
&& ( true == mvControlled->Battery ) ) ) {
if( ( mvControlled->MainsInitTimeCountdown <= 0.0 )
&& ( ( fHVoltage > 0.5 * mvControlled->EnginePowerSource.MaxVoltage )
|| ( ( mvControlled->EngineType != TEngineType::ElectricSeriesMotor )
&& ( mvControlled->EngineType != TEngineType::ElectricInductionMotor )
&& ( true == mvControlled->Battery ) ) ) ) {
// prevent the switch from working if there's no power
// TODO: consider whether it makes sense for diesel engines and such
fMainRelayTimer += Deltatime;
@@ -5640,9 +5671,10 @@ bool TTrain::Update( double const Deltatime )
|| (true == mvControlled->Mains) ) ?
true :
false ) );
// NOTE: 'off' variant uses the same test, but opposite resulting states
btLampkaWylSzybkiOff.Turn(
( ( ( m_linebreakerstate == 2 )
( ( ( mvControlled->MainsInitTimeCountdown > 0.0 )
|| ( fHVoltage == 0.0 )
|| ( m_linebreakerstate == 2 )
|| ( true == mvControlled->Mains ) ) ?
false :
true ) );
@@ -5874,7 +5906,7 @@ bool TTrain::Update( double const Deltatime )
auto const *mover { tmp->MoverParameters };
btLampkaWylSzybkiB.Turn( mover->Mains );
btLampkaWylSzybkiBOff.Turn( false == mover->Mains );
btLampkaWylSzybkiBOff.Turn( ( false == mover->Mains ) && ( mover->MainsInitTimeCountdown <= 0.0 ) && ( fHVoltage != 0.0 ) );
btLampkaOporyB.Turn(mover->ResistorsFlagCheck());
btLampkaBezoporowaB.Turn(
@@ -6129,6 +6161,7 @@ bool TTrain::Update( double const Deltatime )
ggRadioChannelNext.Update();
ggRadioStop.Update();
ggRadioTest.Update();
ggRadioCall3.Update();
ggDepartureSignalButton.Update();
ggPantFrontButton.Update();
@@ -7479,6 +7512,7 @@ void TTrain::clear_cab_controls()
ggRadioChannelNext.Clear();
ggRadioStop.Clear();
ggRadioTest.Clear();
ggRadioCall3.Clear();
ggDoorLeftPermitButton.Clear();
ggDoorRightPermitButton.Clear();
ggDoorPermitPresetButton.Clear();
@@ -7796,7 +7830,7 @@ void TTrain::set_cab_controls( int const Cab ) {
TimetableLightActive ?
1.f :
0.f ) );
// doors
// doors permits
if( ggDoorLeftPermitButton.type() != TGaugeType::push ) {
ggDoorLeftPermitButton.PutValue( mvOccupied->Doors.instances[ ( mvOccupied->ActiveCab == 1 ? side::left : side::right ) ].open_permit ? 1.f : 0.f );
}
@@ -7804,6 +7838,7 @@ void TTrain::set_cab_controls( int const Cab ) {
ggDoorRightPermitButton.PutValue( mvOccupied->Doors.instances[ ( mvOccupied->ActiveCab == 1 ? side::right : side::left ) ].open_permit ? 1.f : 0.f );
}
ggDoorPermitPresetButton.PutValue( mvOccupied->Doors.permit_preset );
// door controls
ggDoorLeftButton.PutValue( mvOccupied->Doors.instances[ ( mvOccupied->ActiveCab == 1 ? side::left : side::right ) ].is_closed ? 0.f : 1.f );
ggDoorRightButton.PutValue( mvOccupied->Doors.instances[ ( mvOccupied->ActiveCab == 1 ? side::right : side::left ) ].is_closed ? 0.f : 1.f );
// door lock
@@ -8164,6 +8199,7 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con
{ "radiochannelnext_sw:", ggRadioChannelNext },
{ "radiostop_sw:", ggRadioStop },
{ "radiotest_sw:", ggRadioTest },
{ "radiocall3_sw:", ggRadioCall3 },
{ "pantfront_sw:", ggPantFrontButton },
{ "pantrear_sw:", ggPantRearButton },
{ "pantfrontoff_sw:", ggPantFrontButtonOff },

View File

@@ -347,6 +347,7 @@ class TTrain
static void OnCommand_radiochanneldecrease( TTrain *Train, command_data const &Command );
static void OnCommand_radiostopsend( TTrain *Train, command_data const &Command );
static void OnCommand_radiostoptest( TTrain *Train, command_data const &Command );
static void OnCommand_radiocall3send( TTrain *Train, command_data const &Command );
static void OnCommand_cabchangeforward( TTrain *Train, command_data const &Command );
static void OnCommand_cabchangebackward( TTrain *Train, command_data const &Command );
static void OnCommand_generictoggle( TTrain *Train, command_data const &Command );
@@ -419,6 +420,7 @@ public: // reszta może by?publiczna
TGauge ggRadioChannelNext;
TGauge ggRadioTest;
TGauge ggRadioStop;
TGauge ggRadioCall3;
TGauge ggUpperLightButton;
TGauge ggLeftLightButton;
TGauge ggRightLightButton;

View File

@@ -101,7 +101,7 @@ bool TTrackFollower::Move(double fDistance, bool bPrimary)
{ // przesuwanie wózka po torach o odległość (fDistance), z wyzwoleniem eventów
// bPrimary=true - jest pierwszą osią w pojeździe, czyli generuje eventy i przepisuje pojazd
// Ra: zwraca false, jeśli pojazd ma być usunięty
auto const ismoving { ( std::abs( fDistance ) > 0.01 ) && ( Owner->GetVelocity() > 0.01 ) };
auto const ismoving { /* ( std::abs( fDistance ) > 0.01 ) && */ ( Owner->GetVelocity() > 0.01 ) };
fDistance *= fDirection; // dystans mnożnony przez kierunek
double s; // roboczy dystans
double dir; // zapamiętany kierunek do sprawdzenia, czy się zmienił

View File

@@ -23,7 +23,7 @@ jobs:
- task: PublishBuildArtifacts@1
inputs:
pathtoPublish: 'build/bin'
artifactName: binaries
artifactName: binaries_linux
displayName: 'Publish binaries'
- job: macos1013
@@ -43,7 +43,7 @@ jobs:
- task: PublishBuildArtifacts@1
inputs:
pathtoPublish: 'build/bin'
artifactName: binaries
artifactName: binaries_macos
displayName: 'Publish binaries'
- job: windows_x64
@@ -64,12 +64,12 @@ jobs:
- task: PublishBuildArtifacts@1
inputs:
pathtoPublish: 'build/bin'
artifactName: binaries
artifactName: binaries_win64
displayName: 'Publish binaries'
- task: PublishBuildArtifacts@1
inputs:
pathtoPublish: 'build/pdb'
artifactName: symbols
artifactName: binaries_win64
displayName: 'Publish symbols'
- job: windows_x32
@@ -90,10 +90,10 @@ jobs:
- task: PublishBuildArtifacts@1
inputs:
pathtoPublish: 'build/bin'
artifactName: binaries
artifactName: binaries_win32
displayName: 'Publish binaries'
- task: PublishBuildArtifacts@1
inputs:
pathtoPublish: 'build/pdb'
artifactName: symbols
artifactName: binaries_win32
displayName: 'Publish symbols'

View File

@@ -125,6 +125,7 @@ commanddescription_sequence Commands_descriptions = {
{ "radiochanneldecrease", command_target::vehicle, command_mode::oneoff },
{ "radiostopsend", command_target::vehicle, command_mode::oneoff },
{ "radiostoptest", command_target::vehicle, command_mode::oneoff },
{ "radiocall3send", command_target::vehicle, command_mode::oneoff },
// TBD, TODO: make cab change controls entity-centric
{ "cabchangeforward", command_target::vehicle, command_mode::oneoff },
{ "cabchangebackward", command_target::vehicle, command_mode::oneoff },

View File

@@ -119,6 +119,7 @@ enum class user_command {
radiochanneldecrease,
radiostopsend,
radiostoptest,
radiocall3send,
cabchangeforward,
cabchangebackward,

View File

@@ -127,6 +127,7 @@ driverkeyboard_input::default_bindings() {
{ user_command::radiochanneldecrease, GLFW_KEY_R },
{ user_command::radiostopsend, GLFW_KEY_PAUSE | keymodifier::shift | keymodifier::control },
{ user_command::radiostoptest, GLFW_KEY_R | keymodifier::shift | keymodifier::control },
{ user_command::radiocall3send, GLFW_KEY_BACKSPACE },
{ user_command::cabchangeforward, GLFW_KEY_HOME },
{ user_command::cabchangebackward, GLFW_KEY_END },
// viewturn,

View File

@@ -719,6 +719,9 @@ drivermouse_input::default_bindings() {
{ "radiotest_sw:", {
user_command::radiostoptest,
user_command::none } },
{ "radiocall3_sw:", {
user_command::radiocall3send,
user_command::none } },
{ "pantfront_sw:", {
user_command::pantographtogglefront,
user_command::none } },

View File

@@ -44,7 +44,7 @@ driver_ui::driver_ui() {
m_scenariopanel.size_max = { Global.iWindowWidth * 0.95, Global.iWindowHeight * 0.95 };
m_timetablepanel.title = STR("%-*.*s Time: %d:%02d:%02d");
m_timetablepanel.size_min = { 435, 110 };
m_timetablepanel.size_min = { 435, 70};
m_timetablepanel.size_max = { 435, Global.iWindowHeight * 0.95 };
m_transcriptspanel.title = STR("Transcripts");

File diff suppressed because it is too large Load Diff

View File

@@ -85,6 +85,7 @@ private:
void update_section_scantable( std::vector<text_line> &Output );
void update_section_scenario( std::vector<text_line> &Output );
void update_section_eventqueue( std::vector<text_line> &Output );
void update_section_powergrid( std::vector<text_line> &Output );
void update_section_camera( std::vector<text_line> &Output );
void update_section_renderer( std::vector<text_line> &Output );
// section update helpers
@@ -103,6 +104,7 @@ private:
m_cameralines,
m_scenariolines,
m_eventqueuelines,
m_powergridlines,
m_rendererlines;
int tprev { 0 }; // poprzedni czas
double VelPrev { 0.0 }; // poprzednia prędkość

View File

@@ -550,6 +550,7 @@ void TTrainParameters::serialize( dictionary_source *Output ) const {
Output->insert( ( stationlabel + "am" ), timetableline.Am );
Output->insert( ( stationlabel + "dh" ), timetableline.Dh );
Output->insert( ( stationlabel + "dm" ), timetableline.Dm );
Output->insert( ( stationlabel + "tracks" ), timetableline.TrackNo );
}
}
}

View File

@@ -124,8 +124,7 @@ class opengl_renderer
/// Renderer runtime settings
struct Settings
{
/** Force normal render of traction, when user is in debug mode. */
bool force_normal_traction_render { false };
bool traction_debug { false };
} settings;
// methods

View File

@@ -531,7 +531,10 @@ state_serializer::deserialize_node( cParser &Input, scene::scratch_data &Scratch
}
else {
scene::Groups.insert( scene::Groups.handle(), eventlauncher );
simulation::Region->insert( eventlauncher );
if( false == eventlauncher->IsRadioActivated() ) {
// NOTE: radio-activated launchers due to potentially large activation radius are resolved on global level rather than put in a region cell
simulation::Region->insert( eventlauncher );
}
}
}
else if( nodedata.type == "sound" ) {

View File

@@ -246,6 +246,7 @@ std::string locale::label_cab_control(std::string const &Label)
{ "radiochannelnext_sw:", STRN("radio channel") },
{ "radiotest_sw:", STRN("radiostop test") },
{ "radiostop_sw:", STRN("radiostop") },
{ "radiocall3_sw:", STRN("radio call 3") },
{ "pantfront_sw:", STRN("pantograph A") },
{ "pantrear_sw:", STRN("pantograph B") },
{ "pantfrontoff_sw:", STRN("pantograph A") },

View File

@@ -1 +1 @@
#define VERSION_INFO "M7 (sim) 23.05.2019"
#define VERSION_INFO "M7 (sim) 13.07.2019"

View File

@@ -74,9 +74,9 @@ void ui::vehicleparams_panel::render_contents()
// power transfers
mover.Couplers[ end::front ].power_high.voltage,
mover.Couplers[ end::front ].power_high.current,
std::string( mover.Couplers[ end::front ].power_high.local ? "" : "-" ).c_str(),
std::string( mover.Couplers[ end::front ].power_high.is_local ? "" : "-" ).c_str(),
std::string( vehicle.DirectionGet() ? ":<<:" : ":>>:" ).c_str(),
std::string( mover.Couplers[ end::rear ].power_high.local ? "" : "-" ).c_str(),
std::string( mover.Couplers[ end::rear ].power_high.is_local ? "" : "-" ).c_str(),
mover.Couplers[ end::rear ].power_high.voltage,
mover.Couplers[ end::rear ].power_high.current );