DirectOutputR1
DirectOutput framework R1 for virtual pinball cabinets.
Go to:
Overview 
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Properties Events Macros Pages
LedWiz.cs
Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Runtime.InteropServices;
5 using System.Threading;
6 using DirectOutput.General.Statistics;
7 
8 namespace DirectOutput.Cab.Out.LW
9 {
25  public class LedWiz : OutputControllerBase, IOutputController, IDisposable
26  {
27  #region Number
28 
29 
30  private object NumberUpdateLocker = new object();
31  private int _Number = -1;
32 
44  public int Number
45  {
46  get { return _Number; }
47  set
48  {
49  if (!value.IsBetween(1, 16))
50  {
51  throw new Exception("LedWiz Numbers must be between 1-16. The supplied number {0} is out of range.".Build(value));
52  }
53  lock (NumberUpdateLocker)
54  {
55  if (_Number != value)
56  {
57 
58  if (Name.IsNullOrWhiteSpace() || Name == "LedWiz {0:00}".Build(_Number))
59  {
60  Name = "LedWiz {0:00}".Build(value);
61  }
62 
63  _Number = value;
64 
65  }
66  }
67  }
68  }
69 
70  #endregion
71 
72 
73 
74  #region IOutputcontroller implementation
75 
76 
77 
78 
79  public override void Update()
80  {
81  LedWizUnits[Number].TriggerLedWizUpdaterThread();
82  }
83 
84 
91  public override void Init(Cabinet Cabinet)
92  {
93  Log.Debug("Initializing LedWiz Nr. {0:00}".Build(Number));
94  AddOutputs();
95  LedWizUnits[Number].Init(Cabinet);
96  Log.Write("LedWiz Nr. {0:00} initialized and updater thread initialized.".Build(Number));
97 
98  }
99 
104  public override void Finish()
105  {
106  Log.Debug("Finishing LedWiz Nr. {0:00}".Build(Number));
107  LedWizUnits[Number].Finish();
108  LedWizUnits[Number].ShutdownLighting();
109  Log.Write("LedWiz Nr. {0:00} finished and updater thread stopped.".Build(Number));
110 
111  }
112  #endregion
113 
114 
115 
116  #region Outputs
117 
118 
119 
124  private void AddOutputs()
125  {
126  for (int i = 1; i <= 32; i++)
127  {
128  if (!Outputs.Any(x => ((LedWizOutput)x).LedWizOutputNumber == i))
129  {
130  Outputs.Add(new LedWizOutput(i) { Name = "{0}.{1:00}".Build(Name, i) });
131  }
132  }
133  }
134 
135 
146  public override void OnOutputValueChanged(IOutput Output)
147  {
148  if (!(Output is LedWizOutput))
149  {
150  throw new Exception("The OutputValueChanged event handler for LedWiz {0:00} has been called by a sender which is not a LedWizOutput.".Build(Number));
151  }
152  LedWizOutput LWO = (LedWizOutput)Output;
153 
154  if (!LWO.LedWizOutputNumber.IsBetween(1, 32))
155  {
156  throw new Exception("LedWiz output numbers must be in the range of 1-32. The supplied output number {0} is out of range.".Build(LWO.LedWizOutputNumber));
157  }
158 
159  LedWizUnit S = LedWizUnits[this.Number];
160  S.UpdateValue(LWO);
161  }
162 
163 
164 
172  //private void OutputValueChanged(object sender, OutputEventArgs e)
173  //{
174 
175  // if (!(e.Output is LedWizOutput))
176  // {
177  // throw new Exception("The OutputValueChanged event handler for LedWiz {0:00} has been called by a sender which is not a LedWizOutput.".Build(Number));
178  // }
179  // LedWizOutput LWO = (LedWizOutput)e.Output;
180 
181  // if (!LWO.LedWizOutputNumber.IsBetween(1, 32))
182  // {
183  // throw new Exception("LedWiz output numbers must be in the range of 1-32. The supplied output number {0} is out of range.".Build(LWO.LedWizOutputNumber));
184  // }
185 
186  // LedWizUnit S = LedWizUnits[this.Number];
187  // S.UpdateValue(LWO);
188  //}
189  #endregion
190 
191 
192 
193  #region LEDWIZ Static Private Methods & Properties
194  [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
195  private struct LWZDEVICELIST
196  {
197  [MarshalAs(UnmanagedType.ByValArray, SizeConst = LWZ_MAX_DEVICES)]
198  public uint[] handles;
199  public int numdevices;
200  }
201 
202  [DllImport("LEDWiz", CallingConvention = CallingConvention.Cdecl)]
203  private extern static void LWZ_SBA(uint device, uint bank0, uint bank1, uint bank2, uint bank3, uint globalPulseSpeed);
204 
205  [DllImport("LEDWiz", CallingConvention = CallingConvention.Cdecl)]
206  private extern static void LWZ_PBA(uint device, uint brightness);
207 
208  [DllImport("LEDWiz", CallingConvention = CallingConvention.Cdecl)]
209  private extern static void LWZ_REGISTER(uint h, uint hwnd);
210 
211  [DllImport("LEDWiz", CallingConvention = CallingConvention.Cdecl)]
212  private extern static void LWZ_SET_NOTIFY(MulticastDelegate notifyProc, uint list);
213 
214  private delegate void NotifyDelegate(int reason, uint newDevice);
215 
216  private static IntPtr MainWindow = IntPtr.Zero;
217 
218  private static LWZDEVICELIST deviceList;
219 
220  private const int LWZ_MAX_DEVICES = 16;
221  private const int LWZ_ADD = 1;
222  private const int LWZ_DELETE = 2;
223 
224  private enum AutoPulseMode : int
225  {
226  RampUpRampDown = 129, // /\/\
227  OnOff = 130, // _|-|_|-|
228  OnRampDown = 131, // -\|-\
229  RampUpDown = 132 // /-|/-
230  }
231 
232 
233 
234  private static void Notify(int reason, uint newDevice)
235  {
236  //TODO: Ledwizverhalten bei add und remove im laufenden betrieb prüfen
237  if (reason == LWZ_ADD)
238  {
239  LWZ_REGISTER(newDevice, (uint)MainWindow.ToInt32());
240  }
241  if (reason == LWZ_DELETE)
242  {
243  }
244  }
245 
246 
247 
248  private static int NumDevices
249  {
250  get { return deviceList.numdevices; }
251  }
252 
253  private static uint[] DeviceHandles
254  {
255  get { return deviceList.handles; }
256  }
257 
258  private static int StartedUp = 0;
259  private static object StartupLocker = new object();
260  private static void StartupLedWiz()
261  {
262  lock (StartupLocker)
263  {
264  if (StartedUp == 0)
265  {
266  MainWindow = IntPtr.Zero;
267  deviceList.handles = new uint[LWZ_MAX_DEVICES] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
268  deviceList.numdevices = 0;
269  try
270  {
271  NotifyDelegate del = new NotifyDelegate(Notify);
272 
273  IntPtr ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(LWZDEVICELIST)));
274  Marshal.StructureToPtr(deviceList, ptr, true);
275  LWZ_SET_NOTIFY(del, (uint)ptr.ToInt32());
276  deviceList = (LWZDEVICELIST)Marshal.PtrToStructure(ptr, typeof(LWZDEVICELIST));
277  Marshal.FreeCoTaskMem(ptr);
278  }
279  catch (Exception ex)
280  {
281  Log.Exception("Could not init LedWiz", ex);
282  throw new Exception("Could not init LedWiz", ex);
283  }
284 
285  }
286  StartedUp++;
287  }
288  }
289 
290  private static void TerminateLedWiz()
291  {
292  lock (StartupLocker)
293  {
294 
295  if (StartedUp > 0)
296  {
297  StartedUp--;
298  if (StartedUp == 0)
299  {
300  foreach (LedWizUnit S in LedWizUnits.Values)
301  {
302  S.ShutdownLighting();
303  }
304  LWZ_SET_NOTIFY((System.MulticastDelegate)null, 0);
305  }
306  }
307  }
308  }
309  #endregion
310 
311 
316  public static List<int> GetLedwizNumbers()
317  {
318  List<int> L = new List<int>();
319 
320  LedWiz LW = new LedWiz();
321  for (int i = 0; i < LedWiz.NumDevices; i++)
322  {
323  L.Add((int)LedWiz.DeviceHandles[i]);
324  }
325  LW.Dispose();
326  return L;
327  }
328 
329 
330 
334  ~LedWiz()
335  {
336 
337  Dispose(false);
338  }
339 
340  #region Dispose
341 
342 
343 
347  public void Dispose()
348  {
349 
350  Dispose(true);
351  GC.SuppressFinalize(this); // remove this from gc finalizer list
352  }
357  protected virtual void Dispose(bool disposing)
358  {
359  // Check to see if Dispose has already been called.
360  if (!this.disposed)
361  {
362  try
363  {
364  Log.Debug("Disposing LedWiz instance {0:00}.".Build(Number));
365  }
366  catch
367  {
368  }
369  // If disposing equals true, dispose all managed
370  // and unmanaged resources.
371  if (disposing)
372  {
373  // Dispose managed resources.
374 
375  }
376 
377  // Call the appropriate methods to clean up
378  // unmanaged resources here.
379  // If disposing is false,
380  // only the following code is executed.
381 
382  TerminateLedWiz();
383 
384 
385  // Note disposing has been done.
386  disposed = true;
387 
388  }
389  }
390  private bool disposed = false;
391 
392  #endregion
393 
394 
395 
396  #region Constructor
397 
398 
402  static LedWiz()
403  {
404  LedWizUnits = new Dictionary<int, LedWizUnit>();
405  for (int i = 1; i <= 16; i++)
406  {
407  LedWizUnits.Add(i, new LedWizUnit(i));
408  }
409  }
410 
414  public LedWiz()
415  {
416  StartupLedWiz();
417 
418  Outputs = new OutputList();
419 
420 
421  }
422 
423 
424 
429  public LedWiz(int Number)
430  : this()
431  {
432  this.Number = Number;
433  }
434 
435  #endregion
436 
437 
438 
439 
440 
441 
442  #region Internal class for LedWiz output states and update methods
443 
444  private static Dictionary<int, LedWizUnit> LedWizUnits = new Dictionary<int, LedWizUnit>();
445 
446  private class LedWizUnit
447  {
448  private Pinball Pinball;
449 
450  private TimeSpanStatisticsItem UpdateTimeStatistics;
451  private TimeSpanStatisticsItem PWMUpdateTimeStatistics;
452  private TimeSpanStatisticsItem OnOffUpdateTimeStatistics;
453 
454  //Used to convert the 0-255 range of output values to LedWiz values in the range of 0-48.
455 // private static readonly byte[] ByteToLedWizValue = { 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 36, 36, 36, 36, 36, 37, 37, 37, 37, 37, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 42, 42, 42, 42, 42, 43, 43, 43, 43, 43, 44, 44, 44, 44, 44, 44, 45, 45, 45, 45, 45, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 48, 48, 48, 48, 48 };
456 
457  private static readonly byte[] ByteToLedWizValue = { 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 36, 36, 36, 36, 36, 36, 37, 37, 37, 37, 37, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 42, 42, 42, 42, 42, 42, 43, 43, 43, 43, 43, 44, 44, 44, 44, 44, 45, 45, 45, 45, 45, 45, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 48, 48, 48, 48, 48, 48 };
458  private const int MaxUpdateFailCount = 5;
459 
460 
461  public int Number { get; private set; }
462 
463  public byte[] NewOutputValues = new byte[32] { 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48 };
464  public byte[] CurrentOuputValues = new byte[32];
465  public byte[] NewAfterValueSwitches = new byte[4];
466  public byte[] NewBeforeValueSwitches = new byte[4];
467  public byte[] CurrentAfterValueSwitches = new byte[4];
468  public byte[] CurrentBeforeValueSwitches = new byte[4];
469  public bool NewValueUpdateRequired = true;
470  public bool NewSwitchUpdateBeforeValueUpdateRequired = true;
471  public bool NewSwitchUpdateAfterValueUpdateRequired = true;
472 
473  public bool CurrentValueUpdateRequired = true;
474  public bool CurrentSwitchUpdateBeforeValueUpdateRequired = true;
475  public bool CurrentSwitchUpdateAfterValueUpdateRequired = true;
476  public bool UpdateRequired = true;
477 
478  public object LedWizUpdateLocker = new object();
479  public object ValueChangeLocker = new object();
480 
481  public Thread LedWizUpdater;
482  public bool KeepLedWizUpdaterAlive = false;
483  public object LedWizUpdaterThreadLocker = new object();
484 
485 
486  public void Init(Cabinet Cabinet)
487  {
488  this.Pinball = Cabinet.Pinball;
489  if (!Pinball.TimeSpanStatistics.Contains("LedWiz {0:00} update calls".Build(Number)))
490  {
491  UpdateTimeStatistics = new TimeSpanStatisticsItem() { Name = "LedWiz {0:00} update calls".Build(Number), GroupName = "OutputControllers - LedWiz" };
492  Pinball.TimeSpanStatistics.Add(UpdateTimeStatistics);
493  }
494  else
495  {
496  UpdateTimeStatistics = Pinball.TimeSpanStatistics["LedWiz {0:00} update calls".Build(Number)];
497  }
498  if (!Pinball.TimeSpanStatistics.Contains("LedWiz {0:00} PWM updates".Build(Number)))
499  {
500  PWMUpdateTimeStatistics = new TimeSpanStatisticsItem() { Name = "LedWiz {0:00} PWM updates".Build(Number), GroupName = "OutputControllers - LedWiz" };
501  Pinball.TimeSpanStatistics.Add(PWMUpdateTimeStatistics);
502  }
503  else
504  {
505  PWMUpdateTimeStatistics = Pinball.TimeSpanStatistics["LedWiz {0:00} PWM updates".Build(Number)];
506  }
507  if (!Pinball.TimeSpanStatistics.Contains("LedWiz {0:00} OnOff updates".Build(Number)))
508  {
509  OnOffUpdateTimeStatistics = new TimeSpanStatisticsItem() { Name = "LedWiz {0:00} OnOff updates".Build(Number), GroupName = "OutputControllers - LedWiz" };
510  Pinball.TimeSpanStatistics.Add(OnOffUpdateTimeStatistics);
511  }
512  else
513  {
514  OnOffUpdateTimeStatistics = Pinball.TimeSpanStatistics["LedWiz {0:00} OnOff updates".Build(Number)];
515  }
516  StartLedWizUpdaterThread();
517  }
518 
519  public void Finish()
520  {
521 
522  TerminateLedWizUpdaterThread();
523  ShutdownLighting();
524  this.Pinball = null;
525  UpdateTimeStatistics = null;
526  PWMUpdateTimeStatistics = null;
527  }
528 
529  public void UpdateValue(LedWizOutput LedWizOutput)
530  {
531  byte V = ByteToLedWizValue[LedWizOutput.Value];
532  bool S = (V != 0);
533 
534  int ZeroBasedOutputNumber = LedWizOutput.LedWizOutputNumber - 1;
535 
536  int ByteNr = ZeroBasedOutputNumber >> 3;
537  int BitNr = ZeroBasedOutputNumber & 7;
538 
539  byte Mask = (byte)(1 << BitNr);
540 
541  lock (ValueChangeLocker)
542  {
543  if (S != ((NewAfterValueSwitches[ByteNr] & Mask) != 0))
544  {
545  //Neeed to adjust switches
546  if (S == false)
547  {
548  //Output will be turned off
549  Mask = (byte)~Mask;
550  NewAfterValueSwitches[ByteNr] &= Mask;
551  NewBeforeValueSwitches[ByteNr] &= Mask;
552  NewSwitchUpdateBeforeValueUpdateRequired = true;
553  UpdateRequired = true;
554  }
555  else
556  {
557  //Output will be turned on
558  if (V != NewOutputValues[ZeroBasedOutputNumber])
559  {
560  //Need to change value before turing on
561  NewOutputValues[ZeroBasedOutputNumber] = V;
562  NewAfterValueSwitches[ByteNr] |= Mask;
563  NewValueUpdateRequired = true;
564  NewSwitchUpdateAfterValueUpdateRequired = true;
565  UpdateRequired = true;
566  }
567  else
568  {
569  //Value is correct, only need to turn on switch
570  NewAfterValueSwitches[ByteNr] |= Mask;
571  NewBeforeValueSwitches[ByteNr] |= Mask;
572  NewSwitchUpdateBeforeValueUpdateRequired = true;
573  UpdateRequired = true;
574  }
575  }
576  }
577  else
578  {
579  if (S == true && V != NewOutputValues[ZeroBasedOutputNumber])
580  {
581  //Only need to adjust value
582  NewOutputValues[ZeroBasedOutputNumber] = V;
583  NewValueUpdateRequired = true;
584  UpdateRequired = true;
585  }
586 
587  }
588  }
589  }
590  public void CopyNewToCurrent()
591  {
592  lock (ValueChangeLocker)
593  {
594 
595  CurrentValueUpdateRequired = NewValueUpdateRequired;
596  CurrentSwitchUpdateBeforeValueUpdateRequired = NewSwitchUpdateBeforeValueUpdateRequired;
597  CurrentSwitchUpdateAfterValueUpdateRequired = NewSwitchUpdateAfterValueUpdateRequired;
598 
599 
600  if (NewValueUpdateRequired)
601  {
602  Array.Copy(NewOutputValues, CurrentOuputValues, NewOutputValues.Length);
603  NewValueUpdateRequired = false;
604  }
605  if (NewSwitchUpdateAfterValueUpdateRequired || NewSwitchUpdateBeforeValueUpdateRequired)
606  {
607  Array.Copy(NewAfterValueSwitches, CurrentAfterValueSwitches, NewAfterValueSwitches.Length);
608  Array.Copy(NewBeforeValueSwitches, CurrentBeforeValueSwitches, NewBeforeValueSwitches.Length);
609  Array.Copy(CurrentAfterValueSwitches, NewBeforeValueSwitches, NewAfterValueSwitches.Length);
610  NewSwitchUpdateAfterValueUpdateRequired = false;
611  NewSwitchUpdateBeforeValueUpdateRequired = false;
612  }
613 
614 
615 
616  }
617  }
618 
619  public bool IsUpdaterThreadAlive
620  {
621  get
622  {
623  if (LedWizUpdater != null)
624  {
625  return LedWizUpdater.IsAlive;
626  }
627  return false;
628  }
629  }
630 
631  public void StartLedWizUpdaterThread()
632  {
633  lock (LedWizUpdaterThreadLocker)
634  {
635  if (!IsUpdaterThreadAlive)
636  {
637  KeepLedWizUpdaterAlive = true;
638  LedWizUpdater = new Thread(LedWizUpdaterDoIt);
639  LedWizUpdater.Name = "LedWiz {0:00} updater thread".Build(Number);
640  LedWizUpdater.Start();
641  }
642  }
643  }
644  public void TerminateLedWizUpdaterThread()
645  {
646  lock (LedWizUpdaterThreadLocker)
647  {
648  if (LedWizUpdater != null)
649  {
650  try
651  {
652  KeepLedWizUpdaterAlive = false;
653  lock (LedWizUpdater)
654  {
655  Monitor.Pulse(LedWizUpdater);
656  }
657  if (!LedWizUpdater.Join(1000))
658  {
659  LedWizUpdater.Abort();
660  }
661 
662  }
663  catch (Exception E)
664  {
665  Log.Exception("A error occurd during termination of {0}.".Build(LedWizUpdater.Name), E);
666  throw new Exception("A error occurd during termination of {0}.".Build(LedWizUpdater.Name), E);
667  }
668  LedWizUpdater = null;
669  }
670  }
671  }
672 
673  bool TriggerUpdate = false;
674  public void TriggerLedWizUpdaterThread()
675  {
676  TriggerUpdate = true;
677  lock (LedWizUpdaterThreadLocker)
678  {
679  Monitor.Pulse(LedWizUpdaterThreadLocker);
680  }
681  }
682 
683 
684  //TODO: Check if thread should really terminate on failed updates
685  private void LedWizUpdaterDoIt()
686  {
687  Log.Write("Updater thread for LedWiz {0:00} started.".Build(Number));
688  Pinball.ThreadInfoList.HeartBeat("LedWiz {0:00}".Build(Number));
689 
690 
691  int FailCnt = 0;
692  while (KeepLedWizUpdaterAlive)
693  {
694  try
695  {
696  if (IsPresent)
697  {
698  UpdateTimeStatistics.MeasurementStart();
699  SendLedWizUpdate();
700  UpdateTimeStatistics.MeasurementStop();
701  }
702  FailCnt = 0;
703  }
704  catch (Exception E)
705  {
706  Log.Exception("A error occured when updating LedWiz Nr. {0}".Build(Number), E);
707  Pinball.ThreadInfoList.RecordException(E);
708  FailCnt++;
709 
710  if (FailCnt > MaxUpdateFailCount)
711  {
712  Log.Exception("More than {0} consecutive updates failed for LedWiz Nr. {1}. Updater thread will terminate.".Build(MaxUpdateFailCount, Number));
713  KeepLedWizUpdaterAlive = false;
714  }
715  }
716  Pinball.ThreadInfoList.HeartBeat();
717  if (KeepLedWizUpdaterAlive)
718  {
719  lock (LedWizUpdaterThreadLocker)
720  {
721  while (!TriggerUpdate && KeepLedWizUpdaterAlive)
722  {
723  Monitor.Wait(LedWizUpdaterThreadLocker, 50); // Lock is released while we’re waiting
724  Pinball.ThreadInfoList.HeartBeat();
725  }
726 
727  }
728 
729  }
730  TriggerUpdate = false;
731  }
732  Pinball.ThreadInfoList.ThreadTerminates();
733  Log.Write("Updater thread for LedWiz {0:00} terminated.".Build(Number));
734  }
735 
736 
737 
738  private DateTime LastUpdate = DateTime.Now;
739  const int MinUpdateIntervalMilliseconds = 1;
740  private void UpdateDelay()
741  {
742  int Ms = (int)DateTime.Now.Subtract(LastUpdate).TotalMilliseconds;
743  if (Ms < MinUpdateIntervalMilliseconds)
744  {
745  Thread.Sleep((MinUpdateIntervalMilliseconds - Ms).Limit(0,MinUpdateIntervalMilliseconds));
746  }
747  LastUpdate = DateTime.Now;
748  }
749 
750  private void SendLedWizUpdate()
751  {
752  if (Number.IsBetween(1, 16))
753  {
754 
755  lock (LedWizUpdateLocker)
756  {
757  lock (ValueChangeLocker)
758  {
759  if (!UpdateRequired) return;
760 
761 
762  CopyNewToCurrent();
763 
764  UpdateRequired = false;
765  }
766 
767 
768  if (CurrentValueUpdateRequired)
769  {
770  if (CurrentSwitchUpdateBeforeValueUpdateRequired)
771  {
772  UpdateDelay();
773  OnOffUpdateTimeStatistics.MeasurementStart();
774  SBA(CurrentBeforeValueSwitches[0], CurrentBeforeValueSwitches[1], CurrentBeforeValueSwitches[2], CurrentBeforeValueSwitches[3], 2);
775  OnOffUpdateTimeStatistics.MeasurementStop();
776  }
777 
778  UpdateDelay();
779  PWMUpdateTimeStatistics.MeasurementStart();
780  PBA(CurrentOuputValues);
781  PWMUpdateTimeStatistics.MeasurementStop();
782  }
783  if (CurrentSwitchUpdateAfterValueUpdateRequired || (CurrentSwitchUpdateBeforeValueUpdateRequired && !NewValueUpdateRequired))
784  {
785  UpdateDelay();
786  OnOffUpdateTimeStatistics.MeasurementStart();
787  SBA(CurrentAfterValueSwitches[0], CurrentAfterValueSwitches[1], CurrentAfterValueSwitches[2], CurrentAfterValueSwitches[3], 2);
788  OnOffUpdateTimeStatistics.MeasurementStop();
789  }
790 
791  }
792  }
793  }
794 
795 
796  public void ShutdownLighting()
797  {
798  SBA(0, 0, 0, 0, 2);
799  }
800 
801 
802  private void SBA(uint bank1, uint bank2, uint bank3, uint bank4, uint globalPulseSpeed)
803  {
804  if (IsPresent)
805  {
806  LWZ_SBA((uint)Number, bank1, bank2, bank3, bank4, globalPulseSpeed);
807  }
808  }
809 
810  private void PBA(byte[] val)
811  {
812  if (IsPresent)
813  {
814  IntPtr ptr = Marshal.AllocCoTaskMem(val.Length);
815  Marshal.Copy(val, 0, ptr, val.Length);
816  LWZ_PBA((uint)Number, (uint)ptr.ToInt32());
817  Marshal.FreeCoTaskMem(ptr);
818  }
819  }
820 
821  private bool IsPresent
822  {
823  get
824  {
825  if (!Number.IsBetween(1, 16)) return false;
826  return DeviceHandles.Any(x => x == Number);
827  }
828  }
829 
830 
831  public LedWizUnit(int Number)
832  {
833  this.Number = Number;
834 
835 
836 
837  }
838 
839  }
840 
841 
842 
843  #endregion
844 
845 
846 
847 
848  }
849 }