WIP
DirectOutput framework for virtual pinball cabinets WIP
Go to:
Overview 
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 
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  private int _MinCommandIntervalMs = 1;
76  private bool MinCommandIntervalMsSet = false;
89  public int MinCommandIntervalMs
90  {
91  get { return _MinCommandIntervalMs; }
92  set
93  {
94  _MinCommandIntervalMs = value.Limit(0, 1000);
95  MinCommandIntervalMsSet = true;
96  }
97  }
98 
99 
100 
101 
102  #region IOutputcontroller implementation
103  public override void Update()
108  {
109  LedWizUnits[Number].TriggerLedWizUpdaterThread();
110  }
111 
112 
119  public override void Init(Cabinet Cabinet)
120  {
121  Log.Debug("Initializing LedWiz Nr. {0:00}".Build(Number));
122  AddOutputs();
123  if (!MinCommandIntervalMsSet && Cabinet.Owner.ConfigurationSettings.ContainsKey("LedWizDefaultMinCommandIntervalMs") && Cabinet.Owner.ConfigurationSettings["LedWizDefaultMinCommandIntervalMs"] is int)
124  {
125  MinCommandIntervalMs = (int)Cabinet.Owner.ConfigurationSettings["LedWizDefaultMinCommandIntervalMs"];
126  }
127 
128  LedWizUnits[Number].Init(Cabinet, MinCommandIntervalMs);
129 
130  Log.Write("LedWiz Nr. {0:00} initialized and updater thread initialized.".Build(Number));
131 
132  }
133 
138  public override void Finish()
139  {
140  Log.Debug("Finishing LedWiz Nr. {0:00}".Build(Number));
141  LedWizUnits[Number].Finish();
142  LedWizUnits[Number].ShutdownLighting();
143  Log.Write("LedWiz Nr. {0:00} finished and updater thread stopped.".Build(Number));
144 
145  }
146  #endregion
147 
148 
149 
150  #region Outputs
151 
152 
153 
158  private void AddOutputs()
159  {
160  for (int i = 1; i <= 32; i++)
161  {
162  if (!Outputs.Any(x => ((LedWizOutput)x).LedWizOutputNumber == i))
163  {
164  Outputs.Add(new LedWizOutput(i) { Name = "{0}.{1:00}".Build(Name, i) });
165  }
166  }
167  }
168 
169 
180  protected override void OnOutputValueChanged(IOutput Output)
181  {
182  if (!(Output is LedWizOutput))
183  {
184  throw new Exception("The OutputValueChanged event handler for LedWiz {0:00} has been called by a sender which is not a LedWizOutput.".Build(Number));
185  }
186  LedWizOutput LWO = (LedWizOutput)Output;
187 
188  if (!LWO.LedWizOutputNumber.IsBetween(1, 32))
189  {
190  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));
191  }
192 
193  LedWizUnit S = LedWizUnits[this.Number];
194  S.UpdateValue(LWO);
195  }
196 
197 
198 
206  //private void OutputValueChanged(object sender, OutputEventArgs e)
207  //{
208 
209  // if (!(e.Output is LedWizOutput))
210  // {
211  // throw new Exception("The OutputValueChanged event handler for LedWiz {0:00} has been called by a sender which is not a LedWizOutput.".Build(Number));
212  // }
213  // LedWizOutput LWO = (LedWizOutput)e.Output;
214 
215  // if (!LWO.LedWizOutputNumber.IsBetween(1, 32))
216  // {
217  // 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));
218  // }
219 
220  // LedWizUnit S = LedWizUnits[this.Number];
221  // S.UpdateValue(LWO);
222  //}
223  #endregion
224 
225 
226 
227  #region LEDWIZ Static Private Methods & Properties
228  [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
229  private struct LWZDEVICELIST
230  {
231  [MarshalAs(UnmanagedType.ByValArray, SizeConst = LWZ_MAX_DEVICES)]
232  public uint[] handles;
233  public int numdevices;
234  }
235 
236  [DllImport("LEDWiz", CallingConvention = CallingConvention.Cdecl)]
237  private extern static void LWZ_SBA(uint device, uint bank0, uint bank1, uint bank2, uint bank3, uint globalPulseSpeed);
238 
239  [DllImport("LEDWiz", CallingConvention = CallingConvention.Cdecl)]
240  private extern static void LWZ_PBA(uint device, uint brightness);
241 
242  [DllImport("LEDWiz", CallingConvention = CallingConvention.Cdecl)]
243  private extern static void LWZ_REGISTER(uint h, uint hwnd);
244 
245  [DllImport("LEDWiz", CallingConvention = CallingConvention.Cdecl)]
246  private extern static void LWZ_SET_NOTIFY(MulticastDelegate notifyProc, uint list);
247 
248  private delegate void NotifyDelegate(int reason, uint newDevice);
249 
250  private static IntPtr MainWindow = IntPtr.Zero;
251 
252  private static LWZDEVICELIST deviceList;
253 
254  private const int LWZ_MAX_DEVICES = 16;
255  private const int LWZ_ADD = 1;
256  private const int LWZ_DELETE = 2;
257 
258  private enum AutoPulseMode : int
259  {
260  RampUpRampDown = 129, // /\/\
261  OnOff = 130, // _|-|_|-|
262  OnRampDown = 131, // -\|-\
263  RampUpDown = 132 // /-|/-
264  }
265 
266 
267 
268  private static void Notify(int reason, uint newDevice)
269  {
270  //TODO: Ledwizverhalten bei add und remove im laufenden betrieb prüfen
271  if (reason == LWZ_ADD)
272  {
273  LWZ_REGISTER(newDevice, (uint)MainWindow.ToInt32());
274  }
275  if (reason == LWZ_DELETE)
276  {
277  }
278  }
279 
280 
281 
282  private static int NumDevices
283  {
284  get { return deviceList.numdevices; }
285  }
286 
287  private static uint[] DeviceHandles
288  {
289  get { return deviceList.handles; }
290  }
291 
292  private static int StartedUp = 0;
293  private static object StartupLocker = new object();
294  private static void StartupLedWiz()
295  {
296  lock (StartupLocker)
297  {
298  if (StartedUp == 0)
299  {
300  MainWindow = IntPtr.Zero;
301  deviceList.handles = new uint[LWZ_MAX_DEVICES] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
302  deviceList.numdevices = 0;
303  try
304  {
305  NotifyDelegate del = new NotifyDelegate(Notify);
306 
307  IntPtr ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(LWZDEVICELIST)));
308  Marshal.StructureToPtr(deviceList, ptr, true);
309  LWZ_SET_NOTIFY(del, (uint)ptr.ToInt32());
310  deviceList = (LWZDEVICELIST)Marshal.PtrToStructure(ptr, typeof(LWZDEVICELIST));
311  Marshal.FreeCoTaskMem(ptr);
312  Log.Debug("Ledwiz devicelist content. Handles: {0}, Num devices: {1}".Build(string.Join(", ", deviceList.handles.Select(H => H.ToString())), deviceList.numdevices));
313  }
314  catch (Exception ex)
315  {
316  Log.Exception("Could not init LedWiz", ex);
317  throw new Exception("Could not init LedWiz", ex);
318  }
319 
320  }
321  StartedUp++;
322  }
323  }
324 
325  private static void TerminateLedWiz()
326  {
327  lock (StartupLocker)
328  {
329 
330  if (StartedUp > 0)
331  {
332  StartedUp--;
333  if (StartedUp == 0)
334  {
335  foreach (LedWizUnit S in LedWizUnits.Values)
336  {
337  S.ShutdownLighting();
338  }
339  LWZ_SET_NOTIFY((System.MulticastDelegate)null, 0);
340  }
341  }
342  }
343  }
344  #endregion
345 
346 
351  public static List<int> GetLedwizNumbers()
352  {
353  List<int> L = new List<int>();
354 
355  LedWiz LW = new LedWiz();
356  for (int i = 0; i < LedWiz.NumDevices; i++)
357  {
358  L.Add((int)LedWiz.DeviceHandles[i]);
359  }
360  LW.Dispose();
361  return L;
362  }
363 
364 
365 
369  ~LedWiz()
370  {
371 
372  Dispose(false);
373  }
374 
375  #region Dispose
376 
377 
378 
382  public void Dispose()
383  {
384 
385  Dispose(true);
386  GC.SuppressFinalize(this); // remove this from gc finalizer list
387  }
392  protected virtual void Dispose(bool disposing)
393  {
394  // Check to see if Dispose has already been called.
395  if (!this.disposed)
396  {
397  try
398  {
399  Log.Debug("Disposing LedWiz instance {0:00}.".Build(Number));
400  }
401  catch
402  {
403  }
404  // If disposing equals true, dispose all managed
405  // and unmanaged resources.
406  if (disposing)
407  {
408  // Dispose managed resources.
409 
410  }
411 
412  // Call the appropriate methods to clean up
413  // unmanaged resources here.
414  // If disposing is false,
415  // only the following code is executed.
416 
417  TerminateLedWiz();
418 
419 
420  // Note disposing has been done.
421  disposed = true;
422 
423  }
424  }
425  private bool disposed = false;
426 
427  #endregion
428 
429 
430 
431  #region Constructor
432 
433 
437  static LedWiz()
438  {
439  LedWizUnits = new Dictionary<int, LedWizUnit>();
440  for (int i = 1; i <= 16; i++)
441  {
442  LedWizUnits.Add(i, new LedWizUnit(i));
443  }
444  }
445 
449  public LedWiz()
450  {
451  StartupLedWiz();
452 
453  Outputs = new OutputList();
454 
455 
456  }
457 
458 
459 
464  public LedWiz(int Number)
465  : this()
466  {
467  this.Number = Number;
468  }
469 
470  #endregion
471 
472 
473 
474 
475 
476 
477  #region Internal class for LedWiz output states and update methods
478 
479  private static Dictionary<int, LedWizUnit> LedWizUnits = new Dictionary<int, LedWizUnit>();
480 
481  private class LedWizUnit
482  {
483  // private Pinball Pinball;
484 
485 
486  //Used to convert the 0-255 range of output values to LedWiz values in the range of 0-48.
487  // 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 };
488 
489  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 };
490  private const int MaxUpdateFailCount = 5;
491 
492 
493  public int Number { get; private set; }
494 
495  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 };
496  public byte[] CurrentOuputValues = new byte[32];
497  public byte[] NewAfterValueSwitches = new byte[4];
498  public byte[] NewBeforeValueSwitches = new byte[4];
499  public byte[] CurrentAfterValueSwitches = new byte[4];
500  public byte[] CurrentBeforeValueSwitches = new byte[4];
501  public bool NewValueUpdateRequired = true;
502  public bool NewSwitchUpdateBeforeValueUpdateRequired = true;
503  public bool NewSwitchUpdateAfterValueUpdateRequired = true;
504 
505  public bool CurrentValueUpdateRequired = true;
506  public bool CurrentSwitchUpdateBeforeValueUpdateRequired = true;
507  public bool CurrentSwitchUpdateAfterValueUpdateRequired = true;
508  public bool UpdateRequired = true;
509 
510  public object LedWizUpdateLocker = new object();
511  public object ValueChangeLocker = new object();
512 
513  public Thread LedWizUpdater;
514  public bool KeepLedWizUpdaterAlive = false;
515  public object LedWizUpdaterThreadLocker = new object();
516 
517 
518  public void Init(Cabinet Cabinet, int MinCommandIntervalMs)
519  {
520  //this.Pinball = Cabinet.Pinball;
521  //if (!Pinball.TimeSpanStatistics.Contains("LedWiz {0:00} update calls".Build(Number)))
522  //{
523  // UpdateTimeStatistics = new TimeSpanStatisticsItem() { Name = "LedWiz {0:00} update calls".Build(Number), GroupName = "OutputControllers - LedWiz" };
524  // Pinball.TimeSpanStatistics.Add(UpdateTimeStatistics);
525  //}
526  //else
527  //{
528  // UpdateTimeStatistics = Pinball.TimeSpanStatistics["LedWiz {0:00} update calls".Build(Number)];
529  //}
530  //if (!Pinball.TimeSpanStatistics.Contains("LedWiz {0:00} PWM updates".Build(Number)))
531  //{
532  // PWMUpdateTimeStatistics = new TimeSpanStatisticsItem() { Name = "LedWiz {0:00} PWM updates".Build(Number), GroupName = "OutputControllers - LedWiz" };
533  // Pinball.TimeSpanStatistics.Add(PWMUpdateTimeStatistics);
534  //}
535  //else
536  //{
537  // PWMUpdateTimeStatistics = Pinball.TimeSpanStatistics["LedWiz {0:00} PWM updates".Build(Number)];
538  //}
539  //if (!Pinball.TimeSpanStatistics.Contains("LedWiz {0:00} OnOff updates".Build(Number)))
540  //{
541  // OnOffUpdateTimeStatistics = new TimeSpanStatisticsItem() { Name = "LedWiz {0:00} OnOff updates".Build(Number), GroupName = "OutputControllers - LedWiz" };
542  // Pinball.TimeSpanStatistics.Add(OnOffUpdateTimeStatistics);
543  //}
544  //else
545  //{
546  // OnOffUpdateTimeStatistics = Pinball.TimeSpanStatistics["LedWiz {0:00} OnOff updates".Build(Number)];
547  //}
548  this.MinCommandIntervalMs = MinCommandIntervalMs;
549  StartLedWizUpdaterThread();
550  }
551 
552  public void Finish()
553  {
554 
555  TerminateLedWizUpdaterThread();
556  ShutdownLighting();
557  //this.Pinball = null;
558 
559  }
560 
561  public void UpdateValue(LedWizOutput LedWizOutput)
562  {
563  byte V = ByteToLedWizValue[LedWizOutput.Value];
564  bool S = (V != 0);
565 
566  int ZeroBasedOutputNumber = LedWizOutput.LedWizOutputNumber - 1;
567 
568  int ByteNr = ZeroBasedOutputNumber >> 3;
569  int BitNr = ZeroBasedOutputNumber & 7;
570 
571  byte Mask = (byte)(1 << BitNr);
572 
573  lock (ValueChangeLocker)
574  {
575  if (S != ((NewAfterValueSwitches[ByteNr] & Mask) != 0))
576  {
577  //Neeed to adjust switches
578  if (S == false)
579  {
580  //Output will be turned off
581  Mask = (byte)~Mask;
582  NewAfterValueSwitches[ByteNr] &= Mask;
583  NewBeforeValueSwitches[ByteNr] &= Mask;
584  NewSwitchUpdateBeforeValueUpdateRequired = true;
585  UpdateRequired = true;
586  }
587  else
588  {
589  //Output will be turned on
590  if (V != NewOutputValues[ZeroBasedOutputNumber])
591  {
592  //Need to change value before turing on
593  NewOutputValues[ZeroBasedOutputNumber] = V;
594  NewAfterValueSwitches[ByteNr] |= Mask;
595  NewValueUpdateRequired = true;
596  NewSwitchUpdateAfterValueUpdateRequired = true;
597  UpdateRequired = true;
598  }
599  else
600  {
601  //Value is correct, only need to turn on switch
602  NewAfterValueSwitches[ByteNr] |= Mask;
603  NewBeforeValueSwitches[ByteNr] |= Mask;
604  NewSwitchUpdateBeforeValueUpdateRequired = true;
605  UpdateRequired = true;
606  }
607  }
608  }
609  else
610  {
611  if (S == true && V != NewOutputValues[ZeroBasedOutputNumber])
612  {
613  //Only need to adjust value
614  NewOutputValues[ZeroBasedOutputNumber] = V;
615  NewValueUpdateRequired = true;
616  UpdateRequired = true;
617  }
618 
619  }
620  }
621  }
622  public void CopyNewToCurrent()
623  {
624  lock (ValueChangeLocker)
625  {
626 
627  CurrentValueUpdateRequired = NewValueUpdateRequired;
628  CurrentSwitchUpdateBeforeValueUpdateRequired = NewSwitchUpdateBeforeValueUpdateRequired;
629  CurrentSwitchUpdateAfterValueUpdateRequired = NewSwitchUpdateAfterValueUpdateRequired;
630 
631 
632  if (NewValueUpdateRequired)
633  {
634  Array.Copy(NewOutputValues, CurrentOuputValues, NewOutputValues.Length);
635  NewValueUpdateRequired = false;
636  }
637  if (NewSwitchUpdateAfterValueUpdateRequired || NewSwitchUpdateBeforeValueUpdateRequired)
638  {
639  Array.Copy(NewAfterValueSwitches, CurrentAfterValueSwitches, NewAfterValueSwitches.Length);
640  Array.Copy(NewBeforeValueSwitches, CurrentBeforeValueSwitches, NewBeforeValueSwitches.Length);
641  Array.Copy(CurrentAfterValueSwitches, NewBeforeValueSwitches, NewAfterValueSwitches.Length);
642  NewSwitchUpdateAfterValueUpdateRequired = false;
643  NewSwitchUpdateBeforeValueUpdateRequired = false;
644  }
645 
646 
647 
648  }
649  }
650 
651  public bool IsUpdaterThreadAlive
652  {
653  get
654  {
655  if (LedWizUpdater != null)
656  {
657  return LedWizUpdater.IsAlive;
658  }
659  return false;
660  }
661  }
662 
663  public void StartLedWizUpdaterThread()
664  {
665  lock (LedWizUpdaterThreadLocker)
666  {
667  if (!IsUpdaterThreadAlive)
668  {
669  KeepLedWizUpdaterAlive = true;
670  LedWizUpdater = new Thread(LedWizUpdaterDoIt);
671  LedWizUpdater.Name = "LedWiz {0:00} updater thread".Build(Number);
672  LedWizUpdater.Start();
673  }
674  }
675  }
676  public void TerminateLedWizUpdaterThread()
677  {
678  // lock (LedWizUpdaterThreadLocker)
679  // {
680  if (LedWizUpdater != null)
681  {
682  try
683  {
684  KeepLedWizUpdaterAlive = false;
685  TriggerLedWizUpdaterThread();
686  if (!LedWizUpdater.Join(1000))
687  {
688  LedWizUpdater.Abort();
689  }
690 
691  }
692  catch (Exception E)
693  {
694  Log.Exception("A error occurd during termination of {0}.".Build(LedWizUpdater.Name), E);
695  throw new Exception("A error occurd during termination of {0}.".Build(LedWizUpdater.Name), E);
696  }
697  LedWizUpdater = null;
698  }
699  // }
700  }
701 
702  bool TriggerUpdate = false;
703  public void TriggerLedWizUpdaterThread()
704  {
705  TriggerUpdate = true;
706  lock (LedWizUpdaterThreadLocker)
707  {
708  Monitor.Pulse(LedWizUpdaterThreadLocker);
709  }
710  }
711 
712 
713  //TODO: Check if thread should really terminate on failed updates
714  private void LedWizUpdaterDoIt()
715  {
716  Log.Write("Updater thread for LedWiz {0:00} started.".Build(Number));
717  //Pinball.ThreadInfoList.HeartBeat("LedWiz {0:00}".Build(Number));
718 
719 
720  int FailCnt = 0;
721  while (KeepLedWizUpdaterAlive)
722  {
723  try
724  {
725  if (IsPresent)
726  {
727 
728  SendLedWizUpdate();
729 
730  }
731  FailCnt = 0;
732  }
733  catch (Exception E)
734  {
735  Log.Exception("A error occured when updating LedWiz Nr. {0}".Build(Number), E);
736  //Pinball.ThreadInfoList.RecordException(E);
737  FailCnt++;
738 
739  if (FailCnt > MaxUpdateFailCount)
740  {
741  Log.Exception("More than {0} consecutive updates failed for LedWiz Nr. {1}. Updater thread will terminate.".Build(MaxUpdateFailCount, Number));
742  KeepLedWizUpdaterAlive = false;
743  }
744  }
745  //Pinball.ThreadInfoList.HeartBeat();
746  if (KeepLedWizUpdaterAlive)
747  {
748  lock (LedWizUpdaterThreadLocker)
749  {
750  while (!TriggerUpdate && KeepLedWizUpdaterAlive)
751  {
752  Monitor.Wait(LedWizUpdaterThreadLocker, 50); // Lock is released while we’re waiting
753  //Pinball.ThreadInfoList.HeartBeat();
754  }
755 
756  }
757 
758  }
759  TriggerUpdate = false;
760  }
761  //Pinball.ThreadInfoList.ThreadTerminates();
762  Log.Write("Updater thread for LedWiz {0:00} terminated.".Build(Number));
763  }
764 
765 
766 
767  private DateTime LastUpdate = DateTime.Now;
768  public int MinCommandIntervalMs = 1;
769  private void UpdateDelay()
770  {
771  int Ms = (int)DateTime.Now.Subtract(LastUpdate).TotalMilliseconds;
772  if (Ms < MinCommandIntervalMs)
773  {
774  Thread.Sleep((MinCommandIntervalMs - Ms).Limit(0, MinCommandIntervalMs));
775  }
776  LastUpdate = DateTime.Now;
777  }
778 
779  private void SendLedWizUpdate()
780  {
781  if (Number.IsBetween(1, 16))
782  {
783 
784  lock (LedWizUpdateLocker)
785  {
786  lock (ValueChangeLocker)
787  {
788  if (!UpdateRequired) return;
789 
790 
791  CopyNewToCurrent();
792 
793  UpdateRequired = false;
794  }
795 
796 
797  if (CurrentValueUpdateRequired)
798  {
799  if (CurrentSwitchUpdateBeforeValueUpdateRequired)
800  {
801  UpdateDelay();
802 
803  SBA(CurrentBeforeValueSwitches[0], CurrentBeforeValueSwitches[1], CurrentBeforeValueSwitches[2], CurrentBeforeValueSwitches[3], 2);
804 
805  }
806 
807  UpdateDelay();
808 
809  PBA(CurrentOuputValues);
810 
811  }
812  if (CurrentSwitchUpdateAfterValueUpdateRequired || (CurrentSwitchUpdateBeforeValueUpdateRequired && !NewValueUpdateRequired))
813  {
814  UpdateDelay();
815 
816  SBA(CurrentAfterValueSwitches[0], CurrentAfterValueSwitches[1], CurrentAfterValueSwitches[2], CurrentAfterValueSwitches[3], 2);
817 
818  }
819 
820  }
821  }
822  }
823 
824 
825  public void ShutdownLighting()
826  {
827  SBA(0, 0, 0, 0, 2);
828  }
829 
830 
831  private void SBA(uint bank1, uint bank2, uint bank3, uint bank4, uint globalPulseSpeed)
832  {
833  if (IsPresent)
834  {
835  LWZ_SBA((uint)Number, bank1, bank2, bank3, bank4, globalPulseSpeed);
836  }
837  }
838 
839  private void PBA(byte[] val)
840  {
841  if (IsPresent)
842  {
843  IntPtr ptr = Marshal.AllocCoTaskMem(val.Length);
844  Marshal.Copy(val, 0, ptr, val.Length);
845  LWZ_PBA((uint)Number, (uint)ptr.ToInt32());
846  Marshal.FreeCoTaskMem(ptr);
847  }
848  }
849 
850  private bool IsPresent
851  {
852  get
853  {
854  if (!Number.IsBetween(1, 16)) return false;
855  return DeviceHandles.Any(x => x == Number);
856  }
857  }
858 
859 
860  public LedWizUnit(int Number)
861  {
862  this.Number = Number;
863 
864 
865 
866  }
867 
868  }
869 
870 
871 
872  #endregion
873 
874 
875 
876 
877  }
878 }
override void Init(Cabinet Cabinet)
Initializes the LedWiz object. This method does also start the workerthread which does the actual up...
Definition: LedWiz.cs:119
The Cabinet object describes the parts of a pinball cabinet (toys, outputcontrollers, outputs and more).
Definition: Cabinet.cs:17
int LedWizOutputNumber
Number of the Output of the LedWiz.
Definition: LEDWizOutput.cs:24
virtual void Dispose(bool disposing)
Releases unmanaged and - optionally - managed resources.
Definition: LedWiz.cs:392
override void OnOutputValueChanged(IOutput Output)
This method is called whenever the value of a output in the Outputs property changes its value...
Definition: LedWiz.cs:180
void Dispose()
Disposes the LedWiz object.
Definition: LedWiz.cs:382
ICabinetOwner Owner
Gets or sets the owner or the cabinet.
Definition: Cabinet.cs:67
List of IOutput objects
Definition: OutputList.cs:13
LedWiz(int Number)
Initializes a new instance of the LedWiz class.
Definition: LedWiz.cs:464
static void Write(string Message)
Writes the specified message to the logfile.
Definition: Log.cs:99
A simple logger used to record important events and exceptions.
Definition: Log.cs:14
static void Debug(string Message="")
Writes the specified debug message to the log file.
Definition: Log.cs:216
Common interface for outputs of any output controller. The Output class implements this interface and...
Definition: IOutput.cs:10
Abstract OutputController base class to be used for IOutputController implementations. Implements IOutputController.
Common interface for all outputcontrollers. Only classes implementing this interface can be used as o...
Dictionary< string, object > ConfigurationSettings
Gets the configuration settings. This dict can contain settings which are used by the output controll...
LedWiz()
Initializes a new instance of the LedWiz class.
Definition: LedWiz.cs:449
Output class for LedWiz output controllers.
Definition: LEDWizOutput.cs:12
The LedWiz is a easy to use outputcontroller with 32 outputs which all support 49 pwm levels with a P...
Definition: LedWiz.cs:27
override void Finish()
Finishes the LedWiz object. Finish does also terminate the workerthread for updates.
Definition: LedWiz.cs:138
static List< int > GetLedwizNumbers()
Gets the numbers of the LedWiz controllers which are connected to the system.
Definition: LedWiz.cs:351
Basic IOutput implementation.
Definition: Output.cs:14