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