2 using System.Collections.Generic;
5 using System.Runtime.InteropServices;
7 using System.Xml.Serialization;
28 private object IdUpdateLocker =
new object();
47 if (!value.IsBetween(1, 4))
49 throw new Exception(
"PacLed64 Ids must be between 1-4. The supplied Id {0} is out of range.".Build(value));
56 if (Name.IsNullOrWhiteSpace() || Name ==
"PacLed64 {0:0}".Build(_Id))
58 Name =
"PacLed64 {0:0}".Build(value);
70 private int _MinUpdateIntervalMs = 0;
84 public int MinUpdateIntervalMs
86 get {
return _MinUpdateIntervalMs; }
87 set { _MinUpdateIntervalMs = value.Limit(0, 1000); }
91 private int _FullUpdateThreshold = 30;
107 public int FullUpdateThreshold
109 get {
return _FullUpdateThreshold; }
110 set { _FullUpdateThreshold = value.Limit(0, 64); }
117 #region IOutputcontroller implementation
118 public override void Update()
123 PacLed64Units[Id].TriggerPacLed64UpdaterThread();
136 PacLed64Units[Id].FullUpdateThreshold = FullUpdateThreshold;
137 PacLed64Units[Id].MinUpdateInterval = TimeSpan.FromMilliseconds(MinUpdateIntervalMs);
138 PacLed64Units[Id].Init(Cabinet);
139 Log.
Write(
"PacLed64 Id:{0} initialized and updater thread started.".Build(Id));
149 PacLed64Units[Id].Finish();
150 PacLed64Units[Id].ShutdownLighting();
151 Log.
Write(
"PacLed64 Id:{0} finished and updater thread stopped.".Build(Id));
165 private void AddOutputs()
167 for (
int i = 1; i <= 64; i++)
169 if (!Outputs.Any(x => (x).Number == i))
171 Outputs.Add(
new Output() { Name =
"{0}.{1:00}".Build(Name, i), Number = i });
194 if (!ON.
Number.IsBetween(1, 64))
196 throw new Exception(
"PacLed64 output numbers must be in the range of 1-64. The supplied output number {0} is out of range.".Build(ON.
Number));
199 PacLed64Unit S = PacLed64Units[this.Id];
271 PacLed64Units =
new Dictionary<int, PacLed64Unit>();
272 for (
int i = 1; i <= 4; i++)
274 PacLed64Units.Add(i,
new PacLed64Unit(i));
309 #region Internal class for PacLed64 output states and update methods
311 private static Dictionary<int, PacLed64Unit> PacLed64Units =
new Dictionary<int, PacLed64Unit>();
313 private class PacLed64Unit
318 private const int MaxUpdateFailCount = 5;
321 public int Id {
get;
private set; }
323 public int FullUpdateThreshold {
get; set; }
324 public TimeSpan MinUpdateInterval {
get; set; }
326 private int Index {
get; set; }
328 private PacDriveSingleton PDSingleton;
330 private byte[] NewValue =
new byte[64];
331 private byte[] CurrentValue =
new byte[64];
334 private byte[] LastValueSent =
new byte[64];
335 private bool[] LastStateSent =
new bool[64];
337 public bool UpdateRequired =
true;
339 public object PacLed64UpdateLocker =
new object();
340 public object ValueChangeLocker =
new object();
342 public Thread PacLed64Updater;
343 public bool KeepPacLed64UpdaterAlive =
false;
344 public object PacLed64UpdaterThreadLocker =
new object();
347 public void Init(Cabinet Cabinet)
377 StartPacLed64UpdaterThread();
383 TerminatePacLed64UpdaterThread();
388 public void UpdateValue(IOutput Output)
391 if (!Output.Number.IsBetween(1, 64))
return;
393 int ZeroBasedOutputNumber = Output.Number - 1;
394 lock (ValueChangeLocker)
396 if (NewValue[ZeroBasedOutputNumber] != Output.Value)
398 NewValue[ZeroBasedOutputNumber] = Output.Value;
399 UpdateRequired =
true;
404 private void CopyNewToCurrent()
406 lock (ValueChangeLocker)
408 Array.Copy(NewValue, CurrentValue, NewValue.Length);
413 public bool IsUpdaterThreadAlive
417 if (PacLed64Updater != null)
419 return PacLed64Updater.IsAlive;
425 public void StartPacLed64UpdaterThread()
427 lock (PacLed64UpdaterThreadLocker)
429 if (!IsUpdaterThreadAlive)
431 KeepPacLed64UpdaterAlive =
true;
432 PacLed64Updater =
new Thread(PacLed64UpdaterDoIt);
433 PacLed64Updater.Name =
"PacLed64 {0:0} updater thread".Build(Id);
434 PacLed64Updater.Start();
438 public void TerminatePacLed64UpdaterThread()
440 lock (PacLed64UpdaterThreadLocker)
442 if (PacLed64Updater != null)
446 KeepPacLed64UpdaterAlive =
false;
447 TriggerPacLed64UpdaterThread();
448 if (!PacLed64Updater.Join(1000))
450 PacLed64Updater.Abort();
456 Log.Exception(
"A error occurd during termination of {0}.".Build(PacLed64Updater.Name), E);
457 throw new Exception(
"A error occurd during termination of {0}.".Build(PacLed64Updater.Name), E);
459 PacLed64Updater = null;
464 bool TriggerUpdate =
false;
465 public void TriggerPacLed64UpdaterThread()
467 TriggerUpdate =
true;
468 lock (PacLed64UpdaterThreadLocker)
470 Monitor.Pulse(PacLed64UpdaterThreadLocker);
476 private void PacLed64UpdaterDoIt()
487 Log.Exception(
"A exception occured while setting the fadetime for pacled64 {0} to 0.".Build(Index), E);
491 while (KeepPacLed64UpdaterAlive)
498 SendPacLed64Update();
505 Log.Exception(
"A error occured when updating PacLed64 {0}".Build(Id), E);
509 if (FailCnt > MaxUpdateFailCount)
511 Log.Exception(
"More than {0} consecutive updates failed for PacLed64 {1}. Updater thread will terminate.".Build(MaxUpdateFailCount, Id));
512 KeepPacLed64UpdaterAlive =
false;
516 if (KeepPacLed64UpdaterAlive)
518 lock (PacLed64UpdaterThreadLocker)
520 while (!TriggerUpdate && KeepPacLed64UpdaterAlive)
522 Monitor.Wait(PacLed64UpdaterThreadLocker, 50);
529 TriggerUpdate =
false;
537 private bool ForceFullUpdate =
true;
538 private void SendPacLed64Update()
543 lock (PacLed64UpdateLocker)
545 lock (ValueChangeLocker)
547 if (!UpdateRequired && !ForceFullUpdate)
return;
551 UpdateRequired =
false;
554 byte IntensityUpdatesRequired = 0;
555 byte StateUpdatesRequired = 0;
556 if (!ForceFullUpdate)
559 for (
int g = 0; g < 8; g++)
562 bool StateUpdateRequired =
false;
563 for (
int p = 0; p < 8; p++)
566 if (CurrentValue[o] > 0)
568 if (CurrentValue[o] != LastValueSent[o])
570 IntensityUpdatesRequired++;
572 else if (!LastStateSent[o])
574 StateUpdateRequired =
true;
577 else if (LastStateSent[o])
579 StateUpdateRequired =
true;
583 if (StateUpdateRequired) StateUpdatesRequired++;
584 StateUpdateRequired =
false;
587 if (ForceFullUpdate || (IntensityUpdatesRequired + StateUpdatesRequired) >= FullUpdateThreshold)
590 EnforceMinUpdateInterval();
591 PDSingleton.PacLed64SetLEDIntensities(Index, CurrentValue);
592 Array.Copy(CurrentValue, LastValueSent, CurrentValue.Length);
593 for (
int i = 0; i < 64; i++)
595 LastStateSent[i] = (LastValueSent[i] > 0);
601 for (
int g = 0; g < 8; g++)
604 bool StateUpdateRequired =
false;
605 for (
int p = 0; p < 8; p++)
608 if (CurrentValue[o] > 0)
610 if (CurrentValue[o] != LastValueSent[o])
612 EnforceMinUpdateInterval();
613 PDSingleton.PacLed64SetLEDIntensity(Index, o, CurrentValue[o]);
614 LastStateSent[o] =
true;
615 LastValueSent[o] = CurrentValue[o];
617 else if (!LastStateSent[o])
620 StateUpdateRequired =
true;
621 LastStateSent[o] =
true;
625 else if (LastStateSent[o])
627 StateUpdateRequired =
true;
628 LastStateSent[o] =
false;
629 LastValueSent[o] = 0;
633 if (StateUpdateRequired)
635 EnforceMinUpdateInterval();
636 PDSingleton.PacLed64SetLEDStates(Index, g + 1, (byte)Mask);
637 StateUpdateRequired =
false;
645 ForceFullUpdate =
false;
649 ForceFullUpdate =
true;
654 public void ShutdownLighting()
656 for (
int g = 0; g <= 8; g++)
658 EnforceMinUpdateInterval();
659 PDSingleton.PacLed64SetLEDStates(0, g, 0);
662 LastStateSent.Fill(
false);
665 private void ResetFadeTime()
667 EnforceMinUpdateInterval();
668 PDSingleton.PacLed64SetLEDFadeTime(Index, 0);
673 DateTime LastCommand = DateTime.MinValue;
674 private void EnforceMinUpdateInterval()
676 if (MinUpdateInterval != TimeSpan.Zero)
678 TimeSpan Interval = DateTime.Now - LastCommand;
679 LastCommand = DateTime.Now;
680 if (Interval < MinUpdateInterval)
682 int Sleepperiod = ((int)(MinUpdateInterval - Interval).TotalMilliseconds).Limit(0,1000);
685 Thread.Sleep(Sleepperiod);
693 private bool IsPresent
697 if (!Id.IsBetween(1, 4))
return false;
703 void Instance_OnPacRemoved(
int Index)
705 this.Index = PDSingleton.PacLed64GetIndexForDeviceId(Id);
708 void Instance_OnPacAttached(
int Index)
710 this.Index = PDSingleton.PacLed64GetIndexForDeviceId(Id);
712 TriggerPacLed64UpdaterThread();
715 private void InitUnit()
719 LastValueSent.Fill((byte)0);
721 PDSingleton.PacLed64SetLEDIntensities(Index, LastValueSent);
722 LastStateSent.Fill(
false);
723 LastValueSent.Fill((byte)0);
728 public PacLed64Unit(
int Id)
732 PDSingleton = PacDriveSingleton.Instance;
733 PDSingleton.OnPacAttached +=
new PacDriveSingleton.PacAttachedDelegate(Instance_OnPacAttached);
734 PDSingleton.OnPacRemoved +=
new PacDriveSingleton.PacRemovedDelegate(Instance_OnPacRemoved);
736 this.Index = PacDriveSingleton.Instance.PacLed64GetIndexForDeviceId(Id);
738 NewValue.Fill((byte)0);
The Cabinet object describes the parts of a pinball cabinet (toys, outputcontrollers, outputs and more).
int Number
Gets or sets the number of the Output object.
override void Finish()
Finishes the PacLed64 object. Finish does also terminate the workerthread.
override void OnOutputValueChanged(IOutput Output)
This method is called whenever the value of a output in the Outputs property changes its value...
override void Init(Cabinet Cabinet)
Initializes the PacLed64 object. This method does also start the workerthread which does the actual ...
static void Write(string Message)
Writes the specified message to the logfile.
A simple logger used to record important events and exceptions.
PacLed64(int Id)
Initializes a new instance of the PacLed64 class.
The PacLed64 is a output controller with 64 outputs all supporting 256 pwm levels with a PWM frequenc...
Common interface for outputs of any output controller. The Output class implements this interface and...
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...
PacLed64()
Initializes a new instance of the PacLed64 class.
The namespace DirectOutput.General contains classes for general use.
Basic IOutput implementation.