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
32 private object NumberUpdateLocker =
new object();
33 private int _Number = -1;
48 get {
return _Number; }
51 if (!value.IsBetween(1, 16))
53 throw new Exception(
"LedWiz Numbers must be between 1-16. The supplied number {0} is out of range.".Build(value));
55 lock (NumberUpdateLocker)
60 if (Name.IsNullOrWhiteSpace() || Name ==
"LedWiz {0:00}".Build(_Number))
62 Name =
"LedWiz {0:00}".Build(value);
76 #region IOutputcontroller implementation
81 public override void Update()
83 LedWizUnits[Number].TriggerLedWizUpdaterThread();
95 Log.
Debug(
"Initializing LedWiz Nr. {0:00}".Build(Number));
97 LedWizUnits[Number].Init(Cabinet);
98 Log.
Write(
"LedWiz Nr. {0:00} initialized and updater thread initialized.".Build(Number));
106 public override void Finish()
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));
126 private void AddOutputs()
128 for (
int i = 1; i <= 32; i++)
130 if (!Outputs.Any(x => ((
LedWizOutput)x).LedWizOutputNumber == i))
132 Outputs.Add(
new LedWizOutput(i) { Name =
"{0}.{1:00}".Build(Name, i) });
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));
154 LedWizOutput LWO = (LedWizOutput)Output;
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));
161 LedWizUnit S = LedWizUnits[this.Number];
195 #region LEDWIZ Static Private Methods & Properties
196 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
197 private struct LWZDEVICELIST
199 [MarshalAs(UnmanagedType.ByValArray, SizeConst = LWZ_MAX_DEVICES)]
200 public uint[] handles;
201 public int numdevices;
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);
207 [DllImport(
"LEDWiz", CallingConvention = CallingConvention.Cdecl)]
208 private extern static void LWZ_PBA(uint device, uint brightness);
210 [DllImport(
"LEDWiz", CallingConvention = CallingConvention.Cdecl)]
211 private extern static void LWZ_REGISTER(uint h, uint hwnd);
213 [DllImport(
"LEDWiz", CallingConvention = CallingConvention.Cdecl)]
214 private extern static void LWZ_SET_NOTIFY(MulticastDelegate notifyProc, uint list);
216 private delegate
void NotifyDelegate(
int reason, uint newDevice);
218 private static IntPtr MainWindow = IntPtr.Zero;
220 private static LWZDEVICELIST deviceList;
222 private const int LWZ_MAX_DEVICES = 16;
223 private const int LWZ_ADD = 1;
224 private const int LWZ_DELETE = 2;
226 private enum AutoPulseMode :
int
228 RampUpRampDown = 129,
236 private static void Notify(
int reason, uint newDevice)
239 if (reason == LWZ_ADD)
241 LWZ_REGISTER(newDevice, (uint)MainWindow.ToInt32());
243 if (reason == LWZ_DELETE)
250 private static int NumDevices
252 get {
return deviceList.numdevices; }
255 private static uint[] DeviceHandles
257 get {
return deviceList.handles; }
260 private static int StartedUp = 0;
261 private static object StartupLocker =
new object();
262 private static void StartupLedWiz()
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;
273 NotifyDelegate del =
new NotifyDelegate(Notify);
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);
283 Log.Exception(
"Could not init LedWiz", ex);
284 throw new Exception(
"Could not init LedWiz", ex);
292 private static void TerminateLedWiz()
302 foreach (LedWizUnit S
in LedWizUnits.Values)
304 S.ShutdownLighting();
306 LWZ_SET_NOTIFY((System.MulticastDelegate)null, 0);
318 public static List<int> GetLedwizNumbers()
320 List<int> L =
new List<int>();
323 for (
int i = 0; i <
LedWiz.NumDevices; i++)
325 L.Add((
int)
LedWiz.DeviceHandles[i]);
349 public void Dispose()
353 GC.SuppressFinalize(
this);
359 protected virtual void Dispose(
bool disposing)
366 Log.
Debug(
"Disposing LedWiz instance {0:00}.".Build(Number));
392 private bool disposed =
false;
406 LedWizUnits =
new Dictionary<int, LedWizUnit>();
407 for (
int i = 1; i <= 16; i++)
409 LedWizUnits.Add(i,
new LedWizUnit(i));
434 this.Number = Number;
444 #region Internal class for LedWiz output states and update methods
446 private static Dictionary<int, LedWizUnit> LedWizUnits =
new Dictionary<int, LedWizUnit>();
448 private class LedWizUnit
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;
463 public int Number {
get;
private set; }
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;
475 public bool CurrentValueUpdateRequired =
true;
476 public bool CurrentSwitchUpdateBeforeValueUpdateRequired =
true;
477 public bool CurrentSwitchUpdateAfterValueUpdateRequired =
true;
478 public bool UpdateRequired =
true;
480 public object LedWizUpdateLocker =
new object();
481 public object ValueChangeLocker =
new object();
483 public Thread LedWizUpdater;
484 public bool KeepLedWizUpdaterAlive =
false;
485 public object LedWizUpdaterThreadLocker =
new object();
488 public void Init(Cabinet Cabinet)
493 UpdateTimeStatistics =
new TimeSpanStatisticsItem() { Name =
"LedWiz {0:00} update calls".Build(Number), GroupName =
"OutputControllers - LedWiz" };
494 Pinball.TimeSpanStatistics.Add(UpdateTimeStatistics);
498 UpdateTimeStatistics = Pinball.TimeSpanStatistics[
"LedWiz {0:00} update calls".Build(Number)];
500 if (!Pinball.TimeSpanStatistics.Contains(
"LedWiz {0:00} PWM updates".Build(Number)))
502 PWMUpdateTimeStatistics =
new TimeSpanStatisticsItem() { Name =
"LedWiz {0:00} PWM updates".Build(Number), GroupName =
"OutputControllers - LedWiz" };
503 Pinball.TimeSpanStatistics.Add(PWMUpdateTimeStatistics);
507 PWMUpdateTimeStatistics = Pinball.TimeSpanStatistics[
"LedWiz {0:00} PWM updates".Build(Number)];
509 if (!Pinball.TimeSpanStatistics.Contains(
"LedWiz {0:00} OnOff updates".Build(Number)))
511 OnOffUpdateTimeStatistics =
new TimeSpanStatisticsItem() { Name =
"LedWiz {0:00} OnOff updates".Build(Number), GroupName =
"OutputControllers - LedWiz" };
512 Pinball.TimeSpanStatistics.Add(OnOffUpdateTimeStatistics);
516 OnOffUpdateTimeStatistics = Pinball.TimeSpanStatistics[
"LedWiz {0:00} OnOff updates".Build(Number)];
518 StartLedWizUpdaterThread();
524 TerminateLedWizUpdaterThread();
527 UpdateTimeStatistics = null;
528 PWMUpdateTimeStatistics = null;
531 public void UpdateValue(LedWizOutput LedWizOutput)
533 byte V = ByteToLedWizValue[LedWizOutput.Value];
536 int ZeroBasedOutputNumber = LedWizOutput.LedWizOutputNumber - 1;
538 int ByteNr = ZeroBasedOutputNumber >> 3;
539 int BitNr = ZeroBasedOutputNumber & 7;
541 byte Mask = (byte)(1 << BitNr);
543 lock (ValueChangeLocker)
545 if (S != ((NewAfterValueSwitches[ByteNr] & Mask) != 0))
552 NewAfterValueSwitches[ByteNr] &= Mask;
553 NewBeforeValueSwitches[ByteNr] &= Mask;
554 NewSwitchUpdateBeforeValueUpdateRequired =
true;
555 UpdateRequired =
true;
560 if (V != NewOutputValues[ZeroBasedOutputNumber])
563 NewOutputValues[ZeroBasedOutputNumber] = V;
564 NewAfterValueSwitches[ByteNr] |= Mask;
565 NewValueUpdateRequired =
true;
566 NewSwitchUpdateAfterValueUpdateRequired =
true;
567 UpdateRequired =
true;
572 NewAfterValueSwitches[ByteNr] |= Mask;
573 NewBeforeValueSwitches[ByteNr] |= Mask;
574 NewSwitchUpdateBeforeValueUpdateRequired =
true;
575 UpdateRequired =
true;
581 if (S ==
true && V != NewOutputValues[ZeroBasedOutputNumber])
584 NewOutputValues[ZeroBasedOutputNumber] = V;
585 NewValueUpdateRequired =
true;
586 UpdateRequired =
true;
592 public void CopyNewToCurrent()
594 lock (ValueChangeLocker)
597 CurrentValueUpdateRequired = NewValueUpdateRequired;
598 CurrentSwitchUpdateBeforeValueUpdateRequired = NewSwitchUpdateBeforeValueUpdateRequired;
599 CurrentSwitchUpdateAfterValueUpdateRequired = NewSwitchUpdateAfterValueUpdateRequired;
602 if (NewValueUpdateRequired)
604 Array.Copy(NewOutputValues, CurrentOuputValues, NewOutputValues.Length);
605 NewValueUpdateRequired =
false;
607 if (NewSwitchUpdateAfterValueUpdateRequired || NewSwitchUpdateBeforeValueUpdateRequired)
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;
621 public bool IsUpdaterThreadAlive
625 if (LedWizUpdater != null)
627 return LedWizUpdater.IsAlive;
633 public void StartLedWizUpdaterThread()
635 lock (LedWizUpdaterThreadLocker)
637 if (!IsUpdaterThreadAlive)
639 KeepLedWizUpdaterAlive =
true;
640 LedWizUpdater =
new Thread(LedWizUpdaterDoIt);
641 LedWizUpdater.Name =
"LedWiz {0:00} updater thread".Build(Number);
642 LedWizUpdater.Start();
646 public void TerminateLedWizUpdaterThread()
648 lock (LedWizUpdaterThreadLocker)
650 if (LedWizUpdater != null)
654 KeepLedWizUpdaterAlive =
false;
657 Monitor.Pulse(LedWizUpdater);
659 if (!LedWizUpdater.Join(1000))
661 LedWizUpdater.Abort();
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);
670 LedWizUpdater = null;
675 bool TriggerUpdate =
false;
676 public void TriggerLedWizUpdaterThread()
678 TriggerUpdate =
true;
679 lock (LedWizUpdaterThreadLocker)
681 Monitor.Pulse(LedWizUpdaterThreadLocker);
687 private void LedWizUpdaterDoIt()
689 Log.Write(
"Updater thread for LedWiz {0:00} started.".Build(Number));
694 while (KeepLedWizUpdaterAlive)
700 UpdateTimeStatistics.MeasurementStart();
702 UpdateTimeStatistics.MeasurementStop();
708 Log.Exception(
"A error occured when updating LedWiz Nr. {0}".Build(Number), E);
712 if (FailCnt > MaxUpdateFailCount)
714 Log.Exception(
"More than {0} consecutive updates failed for LedWiz Nr. {1}. Updater thread will terminate.".Build(MaxUpdateFailCount, Number));
715 KeepLedWizUpdaterAlive =
false;
719 if (KeepLedWizUpdaterAlive)
721 lock (LedWizUpdaterThreadLocker)
723 while (!TriggerUpdate && KeepLedWizUpdaterAlive)
725 Monitor.Wait(LedWizUpdaterThreadLocker, 50);
732 TriggerUpdate =
false;
735 Log.Write(
"Updater thread for LedWiz {0:00} terminated.".Build(Number));
740 private DateTime LastUpdate = DateTime.Now;
741 const int MinUpdateIntervalMilliseconds = 1;
742 private void UpdateDelay()
744 int Ms = (int)DateTime.Now.Subtract(LastUpdate).TotalMilliseconds;
745 if (Ms < MinUpdateIntervalMilliseconds)
747 Thread.Sleep((MinUpdateIntervalMilliseconds - Ms).Limit(0,MinUpdateIntervalMilliseconds));
749 LastUpdate = DateTime.Now;
752 private void SendLedWizUpdate()
754 if (Number.IsBetween(1, 16))
757 lock (LedWizUpdateLocker)
759 lock (ValueChangeLocker)
761 if (!UpdateRequired)
return;
766 UpdateRequired =
false;
770 if (CurrentValueUpdateRequired)
772 if (CurrentSwitchUpdateBeforeValueUpdateRequired)
775 OnOffUpdateTimeStatistics.MeasurementStart();
776 SBA(CurrentBeforeValueSwitches[0], CurrentBeforeValueSwitches[1], CurrentBeforeValueSwitches[2], CurrentBeforeValueSwitches[3], 2);
777 OnOffUpdateTimeStatistics.MeasurementStop();
781 PWMUpdateTimeStatistics.MeasurementStart();
782 PBA(CurrentOuputValues);
783 PWMUpdateTimeStatistics.MeasurementStop();
785 if (CurrentSwitchUpdateAfterValueUpdateRequired || (CurrentSwitchUpdateBeforeValueUpdateRequired && !NewValueUpdateRequired))
788 OnOffUpdateTimeStatistics.MeasurementStart();
789 SBA(CurrentAfterValueSwitches[0], CurrentAfterValueSwitches[1], CurrentAfterValueSwitches[2], CurrentAfterValueSwitches[3],2);
790 OnOffUpdateTimeStatistics.MeasurementStop();
798 public void ShutdownLighting()
804 private void SBA(uint bank1, uint bank2, uint bank3, uint bank4, uint globalPulseSpeed)
808 LWZ_SBA((uint)Number, bank1, bank2, bank3, bank4, globalPulseSpeed);
812 private void PBA(byte[] val)
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);
823 private bool IsPresent
827 if (!Number.IsBetween(1, 16))
return false;
828 return DeviceHandles.Any(x => x == Number);
833 public LedWizUnit(
int Number)
835 this.Number = Number;