DirectOuput Framework R2
DirectOutput framework R2 for virtual pinball cabinets
Go to:
Overview 
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Properties Events Macros Pages
FT245RBitbangController.cs
Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading;
6 
7 namespace DirectOutput.Cab.Out.FTDIChip
8 {
9 
17  {
18  private string _SerialNumber = "";
19 
26  public string SerialNumber
27  {
28  get { return _SerialNumber; }
29  set { _SerialNumber = value; }
30  }
31 
36  public override void Init(Cabinet Cabinet)
37  {
38  if (SerialNumber.IsNullOrWhiteSpace())
39  {
40  Log.Exception("Could not initialize FT245RBitbangController {0}. SerialNumber has not been set.".Build(Name));
41  return;
42  }
43  AddOutputs();
44  InitUpdaterThread();
45  Log.Write("FT245RBitbangController {0} with serial number {1} has been initialized and the updater thread has been started.".Build(Name, SerialNumber));
46  }
47 
52  public override void Finish()
53  {
54  FinishUpdaterThread();
55  Log.Write("FT245RBitbangController {0} with serial number {1} has been finished and the updater thread has been terminated.".Build(Name, SerialNumber));
56  }
57 
61  public override void Update()
62  {
63  if (NewValue != CurrentValue)
64  {
65  UpdaterThreadSignal();
66  }
67  }
68 
73  private void AddOutputs()
74  {
75  for (int i = 1; i <= 8; i++)
76  {
77  if (!Outputs.Any(x => ((OutputNumbered)x).Number == i))
78  {
79  Outputs.Add(new OutputNumbered() { Name = "{0}.{1:00}".Build(Name, i), Number = i });
80  }
81  }
82  }
83 
84 
85 
96  protected override void OnOutputValueChanged(IOutput Output)
97  {
98 
99  if (!(Output is OutputNumbered))
100  {
101  throw new Exception("The OutputValueChanged event handler for the FT245RBitbangController with serial {0} has been called by a sender which is not a OutputNumbered.".Build(SerialNumber));
102  }
103  OutputNumbered ON = (OutputNumbered)Output;
104 
105  if (!ON.Number.IsBetween(1, 64))
106  {
107  throw new Exception("FT245RBitbangController output numbers must be in the range of 1-8. The supplied output number {0} for FT245RBitbangController with serial number {1} is out of range.".Build(ON.Number, SerialNumber));
108  }
109 
110  if (ON.Value > 0)
111  {
112  lock (ValueChangeLocker)
113  {
114  NewValue |= (byte)(1 << (ON.Number - 1));
115  }
116  }
117  else
118  {
119  lock (ValueChangeLocker)
120  {
121  NewValue &= (byte)~(1 << (ON.Number - 1));
122  }
123  }
124  }
125 
126 
127 
128 
129  #region UpdaterThread
130 
131 
132 
133 
134  private void InitUpdaterThread()
135  {
136 
137  if (!UpdaterThreadIsActive)
138  {
139  KeepUpdaterThreadAlive = true;
140  try
141  {
142  UpdaterThread = new Thread(UpdaterThreadDoIt);
143  UpdaterThread.Name = "FT245RBitbangController {0} named {1} updater thread ".Build(SerialNumber, Name);
144  UpdaterThread.Start();
145  }
146  catch (Exception E)
147  {
148  Log.Exception("FT245RBitbangController {0} named {1} updater thread could not start.".Build(SerialNumber, Name), E);
149  throw new Exception("FT245RBitbangController {0} named {1} updater thread could not start.".Build(SerialNumber, Name), E);
150  }
151  }
152  }
153 
158  private void FinishUpdaterThread()
159  {
160  if (UpdaterThread != null)
161  {
162  try
163  {
164  KeepUpdaterThreadAlive = false;
165  lock (UpdaterThreadLocker)
166  {
167  Monitor.Pulse(UpdaterThreadLocker);
168  }
169  if (!UpdaterThread.Join(1000))
170  {
171  UpdaterThread.Abort();
172  }
173  UpdaterThread = null;
174  }
175  catch (Exception E)
176  {
177  Log.Exception("A error occured during termination of FT245RBitbangController ({0}) updater thread.".Build(SerialNumber), E);
178  throw new Exception("A error occured during termination of FT245RBitbangController ({0}) updater thread.".Build(SerialNumber, E));
179  }
180  }
181  }
182 
183 
187  public bool UpdaterThreadIsActive
188  {
189  get
190  {
191  if (UpdaterThread != null)
192  {
193  if (UpdaterThread.IsAlive)
194  {
195  return true;
196  }
197  }
198  return false;
199  }
200  }
201 
205  private void UpdaterThreadSignal()
206  {
207  lock (UpdaterThreadLocker)
208  {
209  Monitor.Pulse(UpdaterThreadLocker);
210  }
211  }
212 
213  private object ValueChangeLocker = new object();
214  private byte NewValue = 0;
215  private byte CurrentValue = 255;
216 
217 
218  private Thread UpdaterThread { get; set; }
219  private object UpdaterThreadLocker = new object();
220  private bool KeepUpdaterThreadAlive = true;
221  private int FirstTryFailCnt = 0;
222  private void UpdaterThreadDoIt()
223  {
224 
225  Connect();
226  if (FTDI == null)
227  {
228  Log.Warning("No connection to FTDI chip {0}. Updater thread will terminate.".Build(SerialNumber));
229  return;
230  }
231 
232  try
233  {
234  SendUpdate(0);
235  }
236  catch (Exception E)
237  {
238  Log.Exception("Could not send initial update to FTDI chip {0}. Updater thread will terminate.".Build(SerialNumber));
239  Disconnect();
240  return;
241  }
242 
243 
244  while (KeepUpdaterThreadAlive)
245  {
246  lock (ValueChangeLocker)
247  {
248  CurrentValue = NewValue;
249  }
250 
251  try
252  {
253  //Try to send the update
254  SendUpdate(CurrentValue);
255  }
256  catch (Exception E)
257  {
258  //Log error for first 5 exceptions
259  if (FirstTryFailCnt < 5)
260  {
261  Log.Exception("Could not send update to FTDI chip {0} on first try. Will reconnect and send update again.".Build(SerialNumber), E);
262  FirstTryFailCnt++;
263  if (FirstTryFailCnt == 5)
264  {
265  Log.Warning("Will not log futher warnings on first try failures when sending data to FTDI chip {0}.".Build(SerialNumber));
266  }
267  }
268 
269 
270  try
271  {
272  //Try to reconnect if update fails
273  Disconnect();
274  Connect();
275  }
276  catch (Exception EC)
277  {
278  //Reconnect failed
279  Log.Exception("Could not send update to FTDI chip {0}. Tried to reconnect to device but failed. Updater thread will terminate.".Build(SerialNumber), EC);
280  break;
281  }
282 
283  if (FTDI != null)
284  {
285  //Reconnected OK. Try again to send value.
286  try
287  {
288  SendUpdate(CurrentValue);
289  }
290  catch (Exception EE)
291  {
292  //Sending value failed again
293  Log.Exception("Could not send update to FTDI chip {0}. Reconnect to device worked, but sending the update did fail again. Updater thread will terminate.".Build(SerialNumber), EE);
294  break;
295  }
296 
297  }
298  else
299  {
300  //Reconnect failed
301  Log.Exception("Could not send update to FTDI chip {0}. Tried to reconnect to device but failed. Updater thread will terminate.".Build(SerialNumber));
302  break;
303  }
304  }
305 
306  if (KeepUpdaterThreadAlive)
307  {
308  lock (UpdaterThreadLocker)
309  {
310  while (NewValue == CurrentValue && KeepUpdaterThreadAlive)
311  {
312  Monitor.Wait(UpdaterThreadLocker, 50); // Lock is released while we’re waiting
313  }
314  }
315  }
316  }
317 
318  try
319  {
320  SendUpdate(0);
321  }
322  catch (Exception E)
323  {
324  Log.Exception("Final update to turn off all output fo FTDI chip {0} failed.".Build(SerialNumber), E);
325  }
326  Disconnect();
327  }
328 
329  #endregion
330 
331  #region FT245R Communication
332 
333  object FTDILocker = new object();
334  FTDI FTDI = null;
335 
336  private void Connect()
337  {
338  lock (FTDILocker)
339  {
340 
341  if (FTDI != null)
342  {
343 
344  Disconnect();
345  }
346 
347  if (SerialNumber.IsNullOrWhiteSpace())
348  {
349  Log.Exception("The SerialNumber has not been set for the FT245RBitbangController named {0}. Cant connect to device.".Build(Name));
350  return;
351  }
352 
353  FTDI = new FTDI();
354 
355  //Try to open the device
356  try
357  {
358  FTDI.ErrorHandler(FTDI.OpenBySerialNumber(SerialNumber));
359  }
360  catch (Exception E)
361  {
362  Log.Exception("Could not open the connection to FTDI chip with serial number {0}.".Build(SerialNumber), E);
363  FTDI = null;
364  return;
365  }
366 
367  // Set FT245RL to synchronous bit-bang mode, used on sainsmart relay board
368  try
369  {
370  FTDI.ErrorHandler(FTDI.SetBitMode(0xFF, FTDI.FT_BIT_MODES.FT_BIT_MODE_SYNC_BITBANG));
371  }
372  catch (Exception E)
373  {
374  Log.Exception("Could set the bitmode to bitbang for FTDI chip with serial number {0}.".Build(SerialNumber), E);
375  Disconnect();
376  FTDI = null;
377  return;
378  }
379 
380  Log.Write("Connection to FTDI chip {0} established.".Build(SerialNumber));
381  }
382 
383 
384 
385 
386  }
387 
388 
389  private void SendUpdate(byte OutputData)
390  {
391  lock (FTDILocker)
392  {
393  if (FTDI != null)
394  {
395  uint numBytes = 0;
396  byte[] Out = { OutputData };
397 
398 
399  FTDI.FT_STATUS Status = FTDI.Write(Out, 1, ref numBytes);
400  if (Status != FTDI.FT_STATUS.FT_OK)
401  {
402 
403  FTDI.ErrorHandler(Status);
404  }
405  if (numBytes != 1)
406  {
407  throw new Exception("Wrong number of written bytes (expected 1, received {0} was returned when sending data to the FTDI chip {1}".Build(numBytes, SerialNumber));
408  }
409  }
410  }
411  }
412 
413  private void Disconnect()
414  {
415  lock (FTDILocker)
416  {
417  if (FTDI != null)
418  {
419 
420  try
421  {
422  SendUpdate(0);
423  }
424  catch { }
425 
426  if (FTDI.IsOpen)
427  {
428  try
429  {
430  FTDI.ErrorHandler(FTDI.Close());
431  }
432  catch (Exception E)
433  {
434  Log.Exception("A exception occured when closing the FTDI chip {0}.".Build(SerialNumber));
435  }
436  }
437  FTDI = null;
438  Log.Write("Connection to FTDI chip {0} closed.".Build(SerialNumber));
439 
440  }
441 
442  }
443 
444  }
445 
446 
447 
448  #endregion
449 
450 
455  {
456  Outputs = new OutputList();
457  }
458 
459  }
460 }