2 using System.Collections.Generic;
4 using System.Runtime.InteropServices;
5 using System.Threading;
6 using DirectOutput.General.Statistics;
8 namespace DirectOutput.Cab.Out.LW
30 private object NumberUpdateLocker =
new object();
31 private int _Number = -1;
46 get {
return _Number; }
49 if (!value.IsBetween(1, 16))
51 throw new Exception(
"LedWiz Numbers must be between 1-16. The supplied number {0} is out of range.".Build(value));
53 lock (NumberUpdateLocker)
58 if (Name.IsNullOrWhiteSpace() || Name ==
"LedWiz {0:00}".Build(_Number))
60 Name =
"LedWiz {0:00}".Build(value);
74 #region IOutputcontroller implementation
79 public override void Update()
81 LedWizUnits[Number].TriggerLedWizUpdaterThread();
93 Log.
Debug(
"Initializing LedWiz Nr. {0:00}".Build(Number));
95 LedWizUnits[Number].Init(Cabinet);
96 Log.
Write(
"LedWiz Nr. {0:00} initialized and updater thread initialized.".Build(Number));
104 public override void Finish()
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));
124 private void AddOutputs()
126 for (
int i = 1; i <= 32; i++)
128 if (!Outputs.Any(x => ((
LedWizOutput)x).LedWizOutputNumber == i))
130 Outputs.Add(
new LedWizOutput(i) { Name =
"{0}.{1:00}".Build(Name, i) });
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));
152 LedWizOutput LWO = (LedWizOutput)Output;
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));
159 LedWizUnit S = LedWizUnits[this.Number];
193 #region LEDWIZ Static Private Methods & Properties
194 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
195 private struct LWZDEVICELIST
197 [MarshalAs(UnmanagedType.ByValArray, SizeConst = LWZ_MAX_DEVICES)]
198 public uint[] handles;
199 public int numdevices;
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);
205 [DllImport(
"LEDWiz", CallingConvention = CallingConvention.Cdecl)]
206 private extern static void LWZ_PBA(uint device, uint brightness);
208 [DllImport(
"LEDWiz", CallingConvention = CallingConvention.Cdecl)]
209 private extern static void LWZ_REGISTER(uint h, uint hwnd);
211 [DllImport(
"LEDWiz", CallingConvention = CallingConvention.Cdecl)]
212 private extern static void LWZ_SET_NOTIFY(MulticastDelegate notifyProc, uint list);
214 private delegate
void NotifyDelegate(
int reason, uint newDevice);
216 private static IntPtr MainWindow = IntPtr.Zero;
218 private static LWZDEVICELIST deviceList;
220 private const int LWZ_MAX_DEVICES = 16;
221 private const int LWZ_ADD = 1;
222 private const int LWZ_DELETE = 2;
224 private enum AutoPulseMode :
int
226 RampUpRampDown = 129,
234 private static void Notify(
int reason, uint newDevice)
237 if (reason == LWZ_ADD)
239 LWZ_REGISTER(newDevice, (uint)MainWindow.ToInt32());
241 if (reason == LWZ_DELETE)
248 private static int NumDevices
250 get {
return deviceList.numdevices; }
253 private static uint[] DeviceHandles
255 get {
return deviceList.handles; }
258 private static int StartedUp = 0;
259 private static object StartupLocker =
new object();
260 private static void StartupLedWiz()
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;
271 NotifyDelegate del =
new NotifyDelegate(Notify);
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);
281 Log.Exception(
"Could not init LedWiz", ex);
282 throw new Exception(
"Could not init LedWiz", ex);
290 private static void TerminateLedWiz()
300 foreach (LedWizUnit S
in LedWizUnits.Values)
302 S.ShutdownLighting();
304 LWZ_SET_NOTIFY((System.MulticastDelegate)null, 0);
316 public static List<int> GetLedwizNumbers()
318 List<int> L =
new List<int>();
321 for (
int i = 0; i <
LedWiz.NumDevices; i++)
323 L.Add((
int)
LedWiz.DeviceHandles[i]);
347 public void Dispose()
351 GC.SuppressFinalize(
this);
357 protected virtual void Dispose(
bool disposing)
364 Log.
Debug(
"Disposing LedWiz instance {0:00}.".Build(Number));
390 private bool disposed =
false;
404 LedWizUnits =
new Dictionary<int, LedWizUnit>();
405 for (
int i = 1; i <= 16; i++)
407 LedWizUnits.Add(i,
new LedWizUnit(i));
432 this.Number = Number;
442 #region Internal class for LedWiz output states and update methods
444 private static Dictionary<int, LedWizUnit> LedWizUnits =
new Dictionary<int, LedWizUnit>();
446 private class LedWizUnit
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;
461 public int Number {
get;
private set; }
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;
473 public bool CurrentValueUpdateRequired =
true;
474 public bool CurrentSwitchUpdateBeforeValueUpdateRequired =
true;
475 public bool CurrentSwitchUpdateAfterValueUpdateRequired =
true;
476 public bool UpdateRequired =
true;
478 public object LedWizUpdateLocker =
new object();
479 public object ValueChangeLocker =
new object();
481 public Thread LedWizUpdater;
482 public bool KeepLedWizUpdaterAlive =
false;
483 public object LedWizUpdaterThreadLocker =
new object();
486 public void Init(Cabinet Cabinet)
491 UpdateTimeStatistics =
new TimeSpanStatisticsItem() { Name =
"LedWiz {0:00} update calls".Build(Number), GroupName =
"OutputControllers - LedWiz" };
492 Pinball.TimeSpanStatistics.Add(UpdateTimeStatistics);
496 UpdateTimeStatistics = Pinball.TimeSpanStatistics[
"LedWiz {0:00} update calls".Build(Number)];
498 if (!Pinball.TimeSpanStatistics.Contains(
"LedWiz {0:00} PWM updates".Build(Number)))
500 PWMUpdateTimeStatistics =
new TimeSpanStatisticsItem() { Name =
"LedWiz {0:00} PWM updates".Build(Number), GroupName =
"OutputControllers - LedWiz" };
501 Pinball.TimeSpanStatistics.Add(PWMUpdateTimeStatistics);
505 PWMUpdateTimeStatistics = Pinball.TimeSpanStatistics[
"LedWiz {0:00} PWM updates".Build(Number)];
507 if (!Pinball.TimeSpanStatistics.Contains(
"LedWiz {0:00} OnOff updates".Build(Number)))
509 OnOffUpdateTimeStatistics =
new TimeSpanStatisticsItem() { Name =
"LedWiz {0:00} OnOff updates".Build(Number), GroupName =
"OutputControllers - LedWiz" };
510 Pinball.TimeSpanStatistics.Add(OnOffUpdateTimeStatistics);
514 OnOffUpdateTimeStatistics = Pinball.TimeSpanStatistics[
"LedWiz {0:00} OnOff updates".Build(Number)];
516 StartLedWizUpdaterThread();
522 TerminateLedWizUpdaterThread();
525 UpdateTimeStatistics = null;
526 PWMUpdateTimeStatistics = null;
529 public void UpdateValue(LedWizOutput LedWizOutput)
531 byte V = ByteToLedWizValue[LedWizOutput.Value];
534 int ZeroBasedOutputNumber = LedWizOutput.LedWizOutputNumber - 1;
536 int ByteNr = ZeroBasedOutputNumber >> 3;
537 int BitNr = ZeroBasedOutputNumber & 7;
539 byte Mask = (byte)(1 << BitNr);
541 lock (ValueChangeLocker)
543 if (S != ((NewAfterValueSwitches[ByteNr] & Mask) != 0))
550 NewAfterValueSwitches[ByteNr] &= Mask;
551 NewBeforeValueSwitches[ByteNr] &= Mask;
552 NewSwitchUpdateBeforeValueUpdateRequired =
true;
553 UpdateRequired =
true;
558 if (V != NewOutputValues[ZeroBasedOutputNumber])
561 NewOutputValues[ZeroBasedOutputNumber] = V;
562 NewAfterValueSwitches[ByteNr] |= Mask;
563 NewValueUpdateRequired =
true;
564 NewSwitchUpdateAfterValueUpdateRequired =
true;
565 UpdateRequired =
true;
570 NewAfterValueSwitches[ByteNr] |= Mask;
571 NewBeforeValueSwitches[ByteNr] |= Mask;
572 NewSwitchUpdateBeforeValueUpdateRequired =
true;
573 UpdateRequired =
true;
579 if (S ==
true && V != NewOutputValues[ZeroBasedOutputNumber])
582 NewOutputValues[ZeroBasedOutputNumber] = V;
583 NewValueUpdateRequired =
true;
584 UpdateRequired =
true;
590 public void CopyNewToCurrent()
592 lock (ValueChangeLocker)
595 CurrentValueUpdateRequired = NewValueUpdateRequired;
596 CurrentSwitchUpdateBeforeValueUpdateRequired = NewSwitchUpdateBeforeValueUpdateRequired;
597 CurrentSwitchUpdateAfterValueUpdateRequired = NewSwitchUpdateAfterValueUpdateRequired;
600 if (NewValueUpdateRequired)
602 Array.Copy(NewOutputValues, CurrentOuputValues, NewOutputValues.Length);
603 NewValueUpdateRequired =
false;
605 if (NewSwitchUpdateAfterValueUpdateRequired || NewSwitchUpdateBeforeValueUpdateRequired)
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;
619 public bool IsUpdaterThreadAlive
623 if (LedWizUpdater != null)
625 return LedWizUpdater.IsAlive;
631 public void StartLedWizUpdaterThread()
633 lock (LedWizUpdaterThreadLocker)
635 if (!IsUpdaterThreadAlive)
637 KeepLedWizUpdaterAlive =
true;
638 LedWizUpdater =
new Thread(LedWizUpdaterDoIt);
639 LedWizUpdater.Name =
"LedWiz {0:00} updater thread".Build(Number);
640 LedWizUpdater.Start();
644 public void TerminateLedWizUpdaterThread()
646 lock (LedWizUpdaterThreadLocker)
648 if (LedWizUpdater != null)
652 KeepLedWizUpdaterAlive =
false;
655 Monitor.Pulse(LedWizUpdater);
657 if (!LedWizUpdater.Join(1000))
659 LedWizUpdater.Abort();
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);
668 LedWizUpdater = null;
673 bool TriggerUpdate =
false;
674 public void TriggerLedWizUpdaterThread()
676 TriggerUpdate =
true;
677 lock (LedWizUpdaterThreadLocker)
679 Monitor.Pulse(LedWizUpdaterThreadLocker);
685 private void LedWizUpdaterDoIt()
687 Log.Write(
"Updater thread for LedWiz {0:00} started.".Build(Number));
688 Pinball.ThreadInfoList.HeartBeat(
"LedWiz {0:00}".Build(Number));
692 while (KeepLedWizUpdaterAlive)
698 UpdateTimeStatistics.MeasurementStart();
700 UpdateTimeStatistics.MeasurementStop();
706 Log.Exception(
"A error occured when updating LedWiz Nr. {0}".Build(Number), E);
707 Pinball.ThreadInfoList.RecordException(E);
710 if (FailCnt > MaxUpdateFailCount)
712 Log.Exception(
"More than {0} consecutive updates failed for LedWiz Nr. {1}. Updater thread will terminate.".Build(MaxUpdateFailCount, Number));
713 KeepLedWizUpdaterAlive =
false;
716 Pinball.ThreadInfoList.HeartBeat();
717 if (KeepLedWizUpdaterAlive)
719 lock (LedWizUpdaterThreadLocker)
721 while (!TriggerUpdate && KeepLedWizUpdaterAlive)
723 Monitor.Wait(LedWizUpdaterThreadLocker, 50);
724 Pinball.ThreadInfoList.HeartBeat();
730 TriggerUpdate =
false;
732 Pinball.ThreadInfoList.ThreadTerminates();
733 Log.Write(
"Updater thread for LedWiz {0:00} terminated.".Build(Number));
738 private DateTime LastUpdate = DateTime.Now;
739 const int MinUpdateIntervalMilliseconds = 1;
740 private void UpdateDelay()
742 int Ms = (int)DateTime.Now.Subtract(LastUpdate).TotalMilliseconds;
743 if (Ms < MinUpdateIntervalMilliseconds)
745 Thread.Sleep((MinUpdateIntervalMilliseconds - Ms).Limit(0,MinUpdateIntervalMilliseconds));
747 LastUpdate = DateTime.Now;
750 private void SendLedWizUpdate()
752 if (Number.IsBetween(1, 16))
755 lock (LedWizUpdateLocker)
757 lock (ValueChangeLocker)
759 if (!UpdateRequired)
return;
764 UpdateRequired =
false;
768 if (CurrentValueUpdateRequired)
770 if (CurrentSwitchUpdateBeforeValueUpdateRequired)
773 OnOffUpdateTimeStatistics.MeasurementStart();
774 SBA(CurrentBeforeValueSwitches[0], CurrentBeforeValueSwitches[1], CurrentBeforeValueSwitches[2], CurrentBeforeValueSwitches[3], 2);
775 OnOffUpdateTimeStatistics.MeasurementStop();
779 PWMUpdateTimeStatistics.MeasurementStart();
780 PBA(CurrentOuputValues);
781 PWMUpdateTimeStatistics.MeasurementStop();
783 if (CurrentSwitchUpdateAfterValueUpdateRequired || (CurrentSwitchUpdateBeforeValueUpdateRequired && !NewValueUpdateRequired))
786 OnOffUpdateTimeStatistics.MeasurementStart();
787 SBA(CurrentAfterValueSwitches[0], CurrentAfterValueSwitches[1], CurrentAfterValueSwitches[2], CurrentAfterValueSwitches[3], 2);
788 OnOffUpdateTimeStatistics.MeasurementStop();
796 public void ShutdownLighting()
802 private void SBA(uint bank1, uint bank2, uint bank3, uint bank4, uint globalPulseSpeed)
806 LWZ_SBA((uint)Number, bank1, bank2, bank3, bank4, globalPulseSpeed);
810 private void PBA(byte[] val)
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);
821 private bool IsPresent
825 if (!Number.IsBetween(1, 16))
return false;
826 return DeviceHandles.Any(x => x == Number);
831 public LedWizUnit(
int Number)
833 this.Number = Number;