forked from tmj-fstate/maszyna
-
Notifications
You must be signed in to change notification settings - Fork 16
/
Console.cpp
377 lines (346 loc) · 14.2 KB
/
Console.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
/*
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/.
*/
#include "stdafx.h"
#include "Console.h"
#include "Globals.h"
#include "application.h"
#include "LPT.h"
#include "Logs.h"
#include "PoKeys55.h"
#include "utilities.h"
//---------------------------------------------------------------------------
// Ra: klasa statyczna gromadząca sygnały sterujące oraz informacje zwrotne
// Ra: stan wejścia zmieniany klawiaturą albo dedykowanym urządzeniem
// Ra: stan wyjścia zmieniany przez symulację (mierniki, kontrolki)
/*******************************
Do klawisza klawiatury przypisana jest maska bitowa oraz numer wejścia.
Naciśnięcie klawisza powoduje wywołanie procedury ustawienia bitu o podanej
masce na podanym wejściu. Zwonienie klawisza analogicznie wywołuje zerowanie
bitu wg maski. Zasadniczo w masce ustawiony jest jeden bit, ale w razie
potrzeby może być ich więcej.
Oddzielne wejścia są wprowadzone po to, by można było używać więcej niż 32
bity do sterowania. Podział na wejścia jest również ze względów organizacyjnych,
np. sterowanie światłami może mieć oddzielny numer wejścia niż przełączanie
radia, ponieważ nie ma potrzeby ich uzależniać (tzn. badać wspólną maskę bitową).
Do każdego wejścia podpięty jest skrypt binarny, charakterystyczny dla danej
konstrukcji pojazdu. Sprawdza on zależności (w tym uszkodzenia) za pomocą
operacji logicznych na maskach bitowych. Do każdego wejścia jest przypisana
jedna, oddzielna maska 32 bit, ale w razie potrzeby istnieje też możliwość
korzystania z masek innych wejść. Skrypt może też wysyłać maski na inne wejścia,
ale należy unikać rekurencji.
Definiowanie wejść oraz przeznaczenia ich masek jest w gestii konstruktora
skryptu. Każdy pojazd może mieć inny schemat wejść i masek, ale w miarę możliwości
należy dążyć do unifikacji. Skrypty mogą również używać dodatkowych masek bitowych.
Maski bitowe odpowiadają stanom przełączników, czujników, styczników itd.
Działanie jest następujące:
- na klawiaturze konsoli naciskany jest przycisk
- naciśnięcie przycisku zamieniane jest na maskę bitową oraz numer wejścia
- wywoływany jest skrypt danego wejścia z ową maską
- skrypt sprawdza zależności i np. modyfikuje własności fizyki albo inne maski
- ewentualnie do wyzwalacza czasowego dodana jest maska i numer wejścia
/*******************************/
// static class member storage allocation
TKeyTrans Console::ktTable[4 * 256];
// Ra: bajer do migania LED-ami w klawiaturze
void SetLedState( unsigned char Code, bool bOn ) {
#ifdef _WIN32
if( bOn != ( ::GetKeyState( Code ) != 0 ) ) {
keybd_event( Code, MapVirtualKey( Code, 0 ), KEYEVENTF_EXTENDEDKEY | 0, 0 );
keybd_event( Code, MapVirtualKey( Code, 0 ), KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0 );
}
#endif
};
//---------------------------------------------------------------------------
int Console::iBits = 0; // zmienna statyczna - obiekt Console jest jednen wspólny
int Console::iMode = 0;
int Console::iConfig = 0;
TPoKeys55 *Console::PoKeys55[2] = {NULL, NULL};
TLPT *Console::LPT = NULL;
int Console::iSwitch[8]; // bistabilne w kabinie, załączane z [Shift], wyłączane bez
int Console::iButton[8]; // monostabilne w kabinie, załączane podczas trzymania klawisza
Console::Console()
{
PoKeys55[0] = PoKeys55[1] = NULL;
for (int i = 0; i < 8; ++i)
{ // zerowanie przełączników
iSwitch[i] = 0; // bity 0..127 - bez [Ctrl], 128..255 - z [Ctrl]
iButton[i] = 0; // bity 0..127 - bez [Shift], 128..255 - z [Shift]
}
};
Console::~Console()
{
Console::Off();
};
void Console::ModeSet(int m, int h)
{ // ustawienie trybu pracy
iMode = m;
iConfig = h;
};
int Console::On()
{ // załączenie konsoli (np. nawiązanie komunikacji)
iSwitch[0] = iSwitch[1] = iSwitch[2] = iSwitch[3] = 0; // bity 0..127 - bez [Ctrl]
iSwitch[4] = iSwitch[5] = iSwitch[6] = iSwitch[7] = 0; // bity 128..255 - z [Ctrl]
switch (iMode)
{
case 1: // kontrolki klawiatury
case 2: // kontrolki klawiatury
iConfig = 0; // licznik użycia Scroll Lock
break;
case 3: // LPT
LPT = new TLPT(); // otwarcie inpout32.dll
if (LPT ? LPT->Connect(iConfig) : false)
{ // wysłać 0?
BitsUpdate(-1); // aktualizacjia stanów, bo przy wczytywaniu mogło być nieaktywne
WriteLog("Feedback Mode 3: InpOut32.dll OK");
}
else
{ // połączenie nie wyszło, ma być NULL
delete LPT;
LPT = NULL;
}
break;
case 4: // PoKeys
PoKeys55[0] = new TPoKeys55();
if (PoKeys55[0] ? PoKeys55[0]->Connect() : false)
{
WriteLog("Found " + PoKeys55[0]->Version());
BitsUpdate(-1); // aktualizacjia stanów, bo przy wczytywaniu mogło być nieaktywne
}
else
{ // połączenie nie wyszło, ma być NULL
delete PoKeys55[0];
PoKeys55[0] = NULL;
WriteLog("PoKeys not found!");
}
break;
}
return 0;
};
void Console::Off()
{ // wyłączenie informacji zwrotnych (reset pulpitu)
BitsClear(-1);
if ((iMode == 1) || (iMode == 2))
if (iConfig & 1) // licznik użycia Scroll Lock
{ // bez sensu to jest, ale mi się samo włącza
SetLedState(VK_SCROLL, true); // przyciśnięty
SetLedState(VK_SCROLL, false); // zwolniony
}
SafeDelete( PoKeys55[0] );
SafeDelete( PoKeys55[1] );
SafeDelete( LPT );
};
void Console::BitsSet(int mask, int entry)
{ // ustawienie bitów o podanej masce (mask) na wejściu (entry)
if ((iBits & mask) != mask) // jeżeli zmiana
{
int old = iBits; // poprzednie stany
iBits |= mask;
BitsUpdate(old ^ iBits); // 1 dla bitów zmienionych
if (iMode == 4)
WriteLog("PoKeys::BitsSet: mask: " + std::to_string(mask) +
" iBits: " + std::to_string(iBits));
}
};
void Console::BitsClear(int mask, int entry)
{ // zerowanie bitów o podanej masce (mask) na wejściu (entry)
if (iBits & mask) // jeżeli zmiana
{
int old = iBits; // poprzednie stany
iBits &= ~mask;
BitsUpdate(old ^ iBits); // 1 dla bitów zmienionych
}
};
void Console::BitsUpdate(int mask)
{ // aktualizacja stanu interfejsu informacji zwrotnej; (mask) - zakres zmienianych bitów
switch (iMode)
{
case 1: // sterowanie światełkami klawiatury: CA/SHP+opory
if( mask & 3 ) {
// gdy SHP albo CA
SetLedState( VK_CAPITAL, ( iBits & 3 ) != 0 );
}
if (mask & 4) {
// gdy jazda na oporach
SetLedState( VK_SCROLL, ( iBits & 4 ) != 0 );
++iConfig; // licznik użycia Scroll Lock
}
break;
case 2: // sterowanie światełkami klawiatury: CA+SHP
if( mask & 2 ) {
// gdy CA
SetLedState( VK_CAPITAL, ( iBits & 2 ) != 0 );
}
if (mask & 1) {
// gdy SHP
SetLedState( VK_SCROLL, ( iBits & 1 ) != 0 );
++iConfig; // licznik użycia Scroll Lock
}
break;
case 3: // LPT Marcela z modyfikacją (jazda na oporach zamiast brzęczyka)
if (LPT)
LPT->Out(iBits);
break;
case 4: // PoKeys55 wg Marcela - wersja druga z końca 2012
if (PoKeys55[0])
{ // pewnie trzeba będzie to dodatkowo buforować i oczekiwać na potwierdzenie
if (mask & 0x0001) // b0 gdy SHP
PoKeys55[0]->Write(0x40, 23 - 1, (iBits & 0x0001) ? 1 : 0);
if (mask & 0x0002) // b1 gdy zmieniony CA
PoKeys55[0]->Write(0x40, 24 - 1, (iBits & 0x0002) ? 1 : 0);
if (mask & 0x0004) // b2 gdy jazda na oporach
PoKeys55[0]->Write(0x40, 32 - 1, (iBits & 0x0004) ? 1 : 0);
if (mask & 0x0008) // b3 Lampka WS (wyłącznika szybkiego)
PoKeys55[0]->Write(0x40, 25 - 1, (iBits & 0x0008) ? 1 : 0);
if (mask & 0x0010) // b4 Lampka przekaźnika nadmiarowego silników trakcyjnych
PoKeys55[0]->Write(0x40, 27 - 1, (iBits & 0x0010) ? 1 : 0);
if (mask & 0x0020) // b5 Lampka styczników liniowych
PoKeys55[0]->Write(0x40, 29 - 1, (iBits & 0x0020) ? 1 : 0);
if (mask & 0x0040) // b6 Lampka poślizgu
PoKeys55[0]->Write(0x40, 30 - 1, (iBits & 0x0040) ? 1 : 0);
if (mask & 0x0080) // b7 Lampka "przetwornicy"
PoKeys55[0]->Write(0x40, 28 - 1, (iBits & 0x0080) ? 1 : 0);
if (mask & 0x0100) // b8 Kontrolka przekaźnika nadmiarowego sprężarki
PoKeys55[0]->Write(0x40, 33 - 1, (iBits & 0x0100) ? 1 : 0);
if (mask & 0x0200) // b9 Kontrolka sygnalizacji wentylatorów i oporów
PoKeys55[0]->Write(0x40, 26 - 1, (iBits & 0x0200) ? 1 : 0);
if (mask & 0x0400) // b10 Kontrolka wysokiego rozruchu
PoKeys55[0]->Write(0x40, 31 - 1, (iBits & 0x0400) ? 1 : 0);
if (mask & 0x0800) // b11 Kontrolka ogrzewania pociągu
PoKeys55[0]->Write(0x40, 34 - 1, (iBits & 0x0800) ? 1 : 0);
if (mask & 0x1000) // b12 Ciśnienie w cylindrach do odbijania w haslerze
PoKeys55[0]->Write(0x40, 52 - 1, (iBits & 0x1000) ? 1 : 0);
if (mask & 0x2000) // b13 Prąd na silnikach do odbijania w haslerze
PoKeys55[0]->Write(0x40, 53 - 1, (iBits & 0x2000) ? 1 : 0);
if (mask & 0x4000) // b14 Brzęczyk SHP lub CA
PoKeys55[0]->Write(0x40, 16 - 1, (iBits & 0x4000) ? 1 : 0);
}
break;
}
};
void Console::ValueSet(int x, double y)
{ // ustawienie wartości (y) na kanale analogowym (x)
if( iMode != 4 ) { return; }
if (PoKeys55[0])
{
x = Global.iPoKeysPWM[x];
if( Global.iCalibrateOutDebugInfo == x ) {
WriteLog( "CalibrateOutDebugInfo: oryginal=" + std::to_string( y ) );
}
if (Global.fCalibrateOutMax[x] > 0)
{
y = clamp( y, 0.0, Global.fCalibrateOutMax[x]);
if( Global.iCalibrateOutDebugInfo == x ) {
WriteLog( " cutted=" + std::to_string( y ) );
}
// sprowadzenie do <0,1> jeśli podana maksymalna wartość
y = y / Global.fCalibrateOutMax[x];
if( Global.iCalibrateOutDebugInfo == x ) {
WriteLog( " fraction=" + std::to_string( y ) );
}
}
double temp = (((((
Global.fCalibrateOut[x][5] * y)
+ Global.fCalibrateOut[x][4]) * y
+ Global.fCalibrateOut[x][3]) * y
+ Global.fCalibrateOut[x][2]) * y
+ Global.fCalibrateOut[x][1]) * y
+ Global.fCalibrateOut[x][0]; // zakres <0;1>
if( Global.iCalibrateOutDebugInfo == x ) {
WriteLog( " calibrated=" + std::to_string( temp ) );
}
PoKeys55[0]->PWM(x, temp);
}
};
void Console::Update()
{ // funkcja powinna być wywoływana regularnie, np. raz w każdej ramce ekranowej
if (iMode == 4)
if (PoKeys55[0])
if (PoKeys55[0]->Update((Global.iPause & 8) > 0))
{ // wykrycie przestawionych przełączników?
Global.iPause &= ~8;
}
else
{ // błąd komunikacji - zapauzować symulację?
if (!(Global.iPause & 8)) // jeśli jeszcze nie oflagowana
Global.iTextMode = GLFW_KEY_F1; // pokazanie czasu/pauzy
Global.iPause |= 8; // tak???
PoKeys55[0]->Connect(); // próba ponownego podłączenia
}
};
float Console::AnalogGet(int x)
{ // pobranie wartości analogowej
if (iMode == 4)
if (PoKeys55[0])
return PoKeys55[0]->fAnalog[x];
return -1.0;
};
float Console::AnalogCalibrateGet(int x)
{ // pobranie i kalibracja wartości analogowej, jeśli nie ma PoKeys zwraca NULL
if (iMode == 4 && PoKeys55[0])
{
float b = PoKeys55[0]->fAnalog[x];
b = (((((
Global.fCalibrateIn[x][5] * b)
+ Global.fCalibrateIn[x][4]) * b
+ Global.fCalibrateIn[x][3]) * b
+ Global.fCalibrateIn[x][2]) * b
+ Global.fCalibrateIn[x][1]) * b
+ Global.fCalibrateIn[x][0];
if (x == 0) return (b + 2) / 8;
if (x == 1) return b/10;
else return b;
}
return -1.0; // odcięcie
};
unsigned char Console::DigitalGet(int x)
{ // pobranie wartości cyfrowej
if (iMode == 4)
if (PoKeys55[0])
return PoKeys55[0]->iInputs[x];
return 0;
};
void Console::OnKeyDown(int k)
{ // naciśnięcie klawisza z powoduje wyłączenie, a
if (k & 0x10000) // jeśli [Shift]
{ // ustawienie bitu w tabeli przełączników bistabilnych
if (k & 0x20000) // jeśli [Ctrl], to zestaw dodatkowy
iSwitch[4 + (char(k) >> 5)] |= 1 << (k & 31); // załącz bistabliny dodatkowy
else
{ // z [Shift] włączenie bitu bistabilnego i dodatkowego monostabilnego
iSwitch[char(k) >> 5] |= 1 << (k & 31); // załącz bistabliny podstawowy
iButton[4 + (char(k) >> 5)] |= (1 << (k & 31)); // załącz monostabilny dodatkowy
}
}
else
{ // zerowanie bitu w tabeli przełączników bistabilnych
if (k & 0x20000) // jeśli [Ctrl], to zestaw dodatkowy
iSwitch[4 + (char(k) >> 5)] &= ~(1 << (k & 31)); // wyłącz bistabilny dodatkowy
else
{
iSwitch[char(k) >> 5] &= ~(1 << (k & 31)); // wyłącz bistabilny podstawowy
iButton[char(k) >> 5] |= 1 << (k & 31); // załącz monostabilny podstawowy
}
}
};
void Console::OnKeyUp(int k)
{ // puszczenie klawisza w zasadzie nie ma znaczenia dla iSwitch, ale zeruje iButton
if ((k & 0x20000) == 0) // monostabilne tylko bez [Ctrl]
if (k & 0x10000) // jeśli [Shift]
iButton[4 + (char(k) >> 5)] &= ~(1 << (k & 31)); // wyłącz monostabilny dodatkowy
else
iButton[char(k) >> 5] &= ~(1 << (k & 31)); // wyłącz monostabilny podstawowy
};
int Console::KeyDownConvert(int k)
{
return int(ktTable[k & 0x3FF].iDown);
};
int Console::KeyUpConvert(int k)
{
return int(ktTable[k & 0x3FF].iUp);
};