-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathFake7.cs
272 lines (240 loc) · 7.63 KB
/
Fake7.cs
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
using GameReaderCommon;
using SimHub.Plugins;
using System;
using System.IO;
using System.IO.Ports;
using System.Threading;
using System.Timers;
namespace blekenbleu
{
// these must be public
public class FakeSettings // saved while plugin restarts
{
public byte[] Value { get; set; } = { 0 };
public string[] Prop { get; set; } = { "","","","","","","","","","" };
}
[PluginDescription("fake serial device plugin to SimHub Custom Serial via com0com null modem")]
[PluginAuthor("blekenbleu")]
[PluginName("Fake8")]
public class Fake7 : IPlugin, IDataPlugin
{
private FakeSettings Settings;
private Fake8 F8;
// configuration source file
internal static readonly string Ini = "DataCorePlugin.ExternalScript.Fake8";
private string[] Label;
static internal bool running;
internal static SerialPort CustomSerial; // SimHub Custom Serial device via com0com
/// <summary>
/// wraps SimHub.Logging.Current.Info() with prefix
/// </summary>
private static bool Info(string str)
{
SimHub.Logging.Current.Info("Fake7." + str); // bool Info()
return true;
}
/// <summary>
/// create SimHub properties
/// </summary>
private void Attach()
{
this.AttachDelegate("Msg_Custom", () => old);
this.AttachDelegate("Msg_Arduino", () => Fake8.msg);
this.AttachDelegate("InitCount", () => Settings.Value[0]);
this.AttachDelegate(Label[0], () => Settings.Prop[0]);
this.AttachDelegate(Label[1], () => Settings.Prop[1]);
this.AttachDelegate(Label[2], () => Settings.Prop[2]);
this.AttachDelegate(Label[3], () => Settings.Prop[3]);
this.AttachDelegate(Label[4], () => Settings.Prop[4]);
this.AttachDelegate(Label[5], () => Settings.Prop[5]);
this.AttachDelegate(Label[6], () => Settings.Prop[6]);
this.AttachDelegate(Label[7], () => Settings.Prop[7]);
this.AttachDelegate(Label[8], () => Settings.Prop[8]);
this.AttachDelegate(Label[9], () => Settings.Prop[9]);
}
/// <summary>
/// match Custom Serial names; save settings
/// </summary>
private bool Parse(string parms)
{
string[] f8 = parms.Split('=');
if (2 == f8.Length && 1 < f8[0].Length)
{
for (byte i = 0; i < Settings.Prop.Length; i++)
if (f8[0] == Label[i])
{
Settings.Prop[i] = f8[1];
return true;
}
old = "Parse(): no match for " + parms;
}
else old = $"Parse(): invalid parm '{parms}'";
return false;
}
/// <summary>
/// declare a delegate for Fake7receiver()
/// </summary>
private delegate void CustDel(Fake7 I, string text);
readonly CustDel Crcv = Fake7receiver;
private Fake7 I() { return this; } // callback for current class instance
/// <summary>
/// Called by delegate from DataReceived method CustomDataReceived(),
/// which runs on a secondary thread; should not directly access main thread variables
/// As a delegate, it must be static and passed the class variables instance... Calls Parse()
/// </summary>
static internal string old;
static private void Fake7receiver(Fake7 I, string msg)
{
if (String.Empty == msg || (old.Length == msg.Length && old == msg))
return;
old = msg;
string[] parm = msg.Split(';');
for (byte i = 0; i < parm.Length; i++)
if (0 < parm[i].Length)
I.Parse(parm[i]);
}
/// <summary>
/// SimHub Custom Serial DataReceived() (via com0com) runs on a secondary thread
/// calls Fake7receiver() via delegate
/// </summary>
private void CustomDataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
while (running)
{
try
{
string s= sp.ReadExisting();
Crcv(I(), s); // pass current instance to Fake7receiver() delegate
}
catch (Exception cre)
{
Info(old = "CustomDataReceived(): " + cre.Message);
running = false; // recovery in Fake8.Fake8receiver()
}
}
}
/// <summary>
/// Log string of known serial ports
/// </summary>
internal void Sports(string n)
{
string s = $"Open(): {n}; available serial ports:";
foreach (string p in SerialPort.GetPortNames())
s += "\n\t" + p;
Info(s);
}
/// <summary>
/// Instance of the current plugin manager
/// </summary>
public PluginManager PluginManager { get; set; }
/// <summary>
/// Called one time per game data update, contains all normalized game data,
/// raw data are "hidden" under a generic object type (A plugin SHOULD NOT USE IT)
///
/// This method must execute as fast as possible and avoid throwing any error
///
/// </summary>
/// <param name="pluginManager"></param>
/// <param name="data">Current game data, including current and previous data frame.</param>
public void DataUpdate(PluginManager pluginManager, ref GameData data)
{
F8.Run(pluginManager); // property changes drive Arduino.Write()
}
/// <summary>
/// Called by End() to close a serial port
/// </summary>
internal void Close(SerialPort serial)
{
if (serial.IsOpen)
try
{
serial.Close();
serial.DiscardInBuffer();
serial.DiscardOutBuffer();
}
catch {/* ignore */}
}
/// <summary>
/// Called at plugin manager stop, close/dispose anything needed here!
/// Plugins are rebuilt at game changes, but NCalc files are not re-read
/// </summary>
/// <param name="pluginManager"></param>
public void End(PluginManager pluginManager)
{
running = false;
this.SaveCommonSettings("GeneralSettings", Settings);
Close(CustomSerial);
F8.End(this);
}
/// <summary>
/// Called by Init() to open a serial port
/// </summary>
internal void Fopen(SerialPort serial, string port)
{
try
{
serial.PortName = port;
serial.BaudRate = 9600;
serial.DtrEnable = true; // nothing received from Arduino without this
serial.RtsEnable = true;
// serial.Handshake = Handshake.None;
serial.Handshake = Handshake.RequestToSend;
serial.ReadTimeout = 50;
serial.WriteTimeout = 250;
serial.Open();
Info($"Open({port}): success!");
}
catch (Exception ex)
{
Sports($"Open({port}) failed. " + ex.Message);
}
}
/// <summary>
/// Called at SimHub start then after game changes
/// </summary>
/// <param name="pluginManager"></param>
public void Init(PluginManager pluginManager)
{
string[] parmArray;
old = "old";
CustomSerial = new SerialPort();
F8 = new Fake8();
// Load settings, create properties
Settings = this.ReadCommonSettings<FakeSettings>("GeneralSettings", () => new FakeSettings());
Info($"Init().InitCount: {++Settings.Value[0]}; Settings.Length = {Settings.Prop.Length}");
if (10 > Settings.Prop.Length)
Settings = new FakeSettings();
Label = new string[Settings.Prop.Length];
// read configuration properties
string parms = pluginManager.GetPropertyValue(Ini + "parms")?.ToString();
byte i = 0;
if (null == parms || 0 == parms.Length)
Info("Init(): missing " + Ini + "parms");
else
{
int n = (parmArray = parms.Split(',')).Length;
if (n > Settings.Prop.Length) {
Info($"Init(): {Ini + "parms"}.Length {n} > {Settings.Prop.Length}");
n = Settings.Prop.Length;
}
for (i = 0; i < n; i++)
Label[i] = parmArray[i];
}
for (; i < Settings.Prop.Length; i++)
Label[i] = "f" + i;
Attach();
// launch serial ports
string null_modem = pluginManager.GetPropertyValue(Ini + "cust")?.ToString();
if (null == null_modem || 0 == null_modem.Length)
Sports("Custom Serial '" + Ini + "cust" + "' missing from F8.ini");
else
{
running = true;
CustomSerial.DataReceived += CustomDataReceived;
Fopen(CustomSerial, null_modem);
F8.Init(pluginManager, this);
}
} // Init()
}
}