2 using System.Collections.Generic;
5 using System.Runtime.InteropServices;
6 using System.Threading;
7 using System.Xml.Serialization;
8 using DirectOutput.General;
9 using DirectOutput.General.Statistics;
11 namespace DirectOutput.Cab.Out.Pac
29 private object IdUpdateLocker =
new object();
48 if (!value.IsBetween(1, 4))
50 throw new Exception(
"PacLed64 Ids must be between 1-4. The supplied Id {0} is out of range.".Build(value));
57 if (Name.IsNullOrWhiteSpace() || Name ==
"PacLed64 {0:0}".Build(_Id))
59 Name =
"PacLed64 {0:0}".Build(value);
73 #region IOutputcontroller implementation
77 public override void Update()
79 PacLed64Units[Id].TriggerPacLed64UpdaterThread();
92 PacLed64Units[Id].Init(Cabinet);
93 Log.
Write(
"PacLed64 Id:{0} initialized and updater thread started.".Build(Id));
101 public override void Finish()
103 PacLed64Units[Id].Finish();
104 PacLed64Units[Id].ShutdownLighting();
105 Log.
Write(
"PacLed64 Id:{0} finished and updater thread stopped.".Build(Id));
119 private void AddOutputs()
121 for (
int i = 1; i <= 64; i++)
125 Outputs.Add(
new OutputNumbered() { Name =
"{0}.{1:00}".Build(Name, i), Number = i });
148 throw new Exception(
"The OutputValueChanged event handler for PacLed64 unit {0} (Id: {2:0}) has been called by a sender which is not a OutputNumbered.".Build(Name, Id));
150 OutputNumbered ON = (OutputNumbered)Output;
152 if (!ON.
Number.IsBetween(1, 64))
154 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));
157 PacLed64Unit S = PacLed64Units[this.Id];
229 PacLed64Units =
new Dictionary<int, PacLed64Unit>();
230 for (
int i = 1; i <= 4; i++)
232 PacLed64Units.Add(i,
new PacLed64Unit(i));
267 #region Internal class for PacLed64 output states and update methods
269 private static Dictionary<int, PacLed64Unit> PacLed64Units =
new Dictionary<int, PacLed64Unit>();
271 private class PacLed64Unit
279 private const int MaxUpdateFailCount = 5;
282 public int Id {
get;
private set; }
284 private int Index {
get;
set; }
286 private PacDriveSingleton PDSingleton;
288 private byte[] NewValue =
new byte[64];
289 private byte[] CurrentValue =
new byte[64];
292 private byte[] LastValueSent =
new byte[64];
293 private bool[] LastStateSent =
new bool[64];
295 public bool UpdateRequired =
true;
297 public object PacLed64UpdateLocker =
new object();
298 public object ValueChangeLocker =
new object();
300 public Thread PacLed64Updater;
301 public bool KeepPacLed64UpdaterAlive =
false;
302 public object PacLed64UpdaterThreadLocker =
new object();
310 UpdateTimeStatistics =
new TimeSpanStatisticsItem() { Name =
"PacLed64 {0:0} update calls".Build(Id), GroupName =
"OutputControllers - PacLed64" };
315 UpdateTimeStatistics = Pinball.TimeSpanStatistics[
"PacLed64 {0:0} update calls".Build(Id)];
317 if (!Pinball.TimeSpanStatistics.Contains(
"PacLed64 {0:0} PWM updates".Build(Id)))
319 PWMUpdateTimeStatistics =
new TimeSpanStatisticsItem() { Name =
"PacLed64 {0:0} PWM updates".Build(Id), GroupName =
"OutputControllers - PacLed64" };
320 Pinball.TimeSpanStatistics.Add(PWMUpdateTimeStatistics);
324 PWMUpdateTimeStatistics = Pinball.TimeSpanStatistics[
"PacLed64 {0:0} PWM updates".Build(Id)];
326 if (!Pinball.TimeSpanStatistics.Contains(
"PacLed64 {0:0} OnOff updates".Build(Id)))
328 OnOffUpdateTimeStatistics =
new TimeSpanStatisticsItem() { Name =
"PacLed64 {0:0} OnOff updates".Build(Id), GroupName =
"OutputControllers - PacLed64" };
329 Pinball.TimeSpanStatistics.Add(OnOffUpdateTimeStatistics);
333 OnOffUpdateTimeStatistics = Pinball.TimeSpanStatistics[
"PacLed64 {0:0} OnOff updates".Build(Id)];
335 StartPacLed64UpdaterThread();
341 TerminatePacLed64UpdaterThread();
344 UpdateTimeStatistics = null;
345 PWMUpdateTimeStatistics = null;
348 public void UpdateValue(OutputNumbered OutputNumbered)
351 if (!OutputNumbered.Number.IsBetween(1, 64))
return;
353 int ZeroBasedOutputNumber = OutputNumbered.Number - 1;
354 lock (ValueChangeLocker)
356 if (NewValue[ZeroBasedOutputNumber] != OutputNumbered.Value)
358 NewValue[ZeroBasedOutputNumber] = OutputNumbered.Value;
359 UpdateRequired =
true;
364 private void CopyNewToCurrent()
366 lock (ValueChangeLocker)
368 Array.Copy(NewValue, CurrentValue, NewValue.Length);
373 public bool IsUpdaterThreadAlive
377 if (PacLed64Updater != null)
379 return PacLed64Updater.IsAlive;
385 public void StartPacLed64UpdaterThread()
387 lock (PacLed64UpdaterThreadLocker)
389 if (!IsUpdaterThreadAlive)
391 KeepPacLed64UpdaterAlive =
true;
392 PacLed64Updater =
new Thread(PacLed64UpdaterDoIt);
393 PacLed64Updater.Name =
"PacLed64 {0:0} updater thread".Build(Id);
394 PacLed64Updater.Start();
398 public void TerminatePacLed64UpdaterThread()
400 lock (PacLed64UpdaterThreadLocker)
402 if (PacLed64Updater != null)
406 KeepPacLed64UpdaterAlive =
false;
407 lock (PacLed64Updater)
409 Monitor.Pulse(PacLed64Updater);
411 if (!PacLed64Updater.Join(1000))
413 PacLed64Updater.Abort();
419 Log.Exception(
"A error occurd during termination of {0}.".Build(PacLed64Updater.Name), E);
420 throw new Exception(
"A error occurd during termination of {0}.".Build(PacLed64Updater.Name), E);
422 PacLed64Updater = null;
427 bool TriggerUpdate =
false;
428 public void TriggerPacLed64UpdaterThread()
430 TriggerUpdate =
true;
431 lock (PacLed64UpdaterThreadLocker)
433 Monitor.Pulse(PacLed64UpdaterThreadLocker);
439 private void PacLed64UpdaterDoIt()
445 while (KeepPacLed64UpdaterAlive)
451 UpdateTimeStatistics.MeasurementStart();
452 SendPacLed64Update();
453 UpdateTimeStatistics.MeasurementStop();
459 Log.Exception(
"A error occured when updating PacLed64 {0}".Build(Id), E);
463 if (FailCnt > MaxUpdateFailCount)
465 Log.Exception(
"More than {0} consecutive updates failed for PacLed64 {1}. Updater thread will terminate.".Build(MaxUpdateFailCount, Id));
466 KeepPacLed64UpdaterAlive =
false;
470 if (KeepPacLed64UpdaterAlive)
472 lock (PacLed64UpdaterThreadLocker)
474 while (!TriggerUpdate && KeepPacLed64UpdaterAlive)
476 Monitor.Wait(PacLed64UpdaterThreadLocker, 50);
483 TriggerUpdate =
false;
490 private bool ForceFullUpdate =
true;
491 private void SendPacLed64Update()
496 lock (PacLed64UpdateLocker)
498 lock (ValueChangeLocker)
500 if (!UpdateRequired && !ForceFullUpdate)
return;
504 UpdateRequired =
false;
507 byte IntensityUpdatesRequired = 0;
508 byte StateUpdatesRequired = 0;
509 if (!ForceFullUpdate)
512 for (
int g = 0; g < 8; g++)
515 bool StateUpdateRequired =
false;
516 for (
int p = 0; p < 8; p++)
519 if (CurrentValue[o] > 0)
521 if (CurrentValue[o] != LastValueSent[o])
523 IntensityUpdatesRequired++;
525 else if (!LastStateSent[o])
527 StateUpdateRequired =
true;
530 else if (LastStateSent[o])
532 StateUpdateRequired =
true;
536 if (StateUpdateRequired) StateUpdatesRequired++;
537 StateUpdateRequired =
false;
540 if ( ForceFullUpdate || (IntensityUpdatesRequired + StateUpdatesRequired) > 30)
543 PDSingleton.PacLed64SetLEDIntensities(Index, CurrentValue);
544 Array.Copy(CurrentValue, LastValueSent, CurrentValue.Length);
545 for (
int i = 0; i < 64; i++)
547 LastStateSent[i] = (LastValueSent[i] > 0);
553 for (
int g = 0; g < 8; g++)
556 bool StateUpdateRequired =
false;
557 for (
int p = 0; p < 8; p++)
560 if (CurrentValue[o] > 0)
562 if (CurrentValue[o] != LastValueSent[o])
564 PDSingleton.PacLed64SetLEDIntensity(Index, o, CurrentValue[o]);
565 LastStateSent[o] =
true;
566 LastValueSent[o] = CurrentValue[o];
568 else if (!LastStateSent[o])
571 StateUpdateRequired =
true;
572 LastStateSent[o] =
true;
575 else if (LastStateSent[o])
577 StateUpdateRequired =
true;
578 LastStateSent[o] =
false;
579 LastValueSent[o] = 0;
583 if (StateUpdateRequired)
585 PDSingleton.PacLed64SetLEDStates(Index, g + 1, (byte)Mask);
586 StateUpdateRequired =
false;
594 ForceFullUpdate =
false;
598 ForceFullUpdate=
true;
603 public void ShutdownLighting()
605 PDSingleton.PacLed64SetLEDStates(0, 0, 0);
606 LastStateSent.Fill(
false);
612 private bool IsPresent
616 if (!Id.IsBetween(1, 4))
return false;
622 void Instance_OnPacRemoved(
int Index)
624 this.Index = PDSingleton.PacLed64GetIndexForDeviceId(Id);
627 void Instance_OnPacAttached(
int Index)
629 this.Index = PDSingleton.PacLed64GetIndexForDeviceId(Id);
631 TriggerPacLed64UpdaterThread();
634 private void InitUnit()
638 LastValueSent.Fill((byte)0);
640 PDSingleton.PacLed64SetLEDIntensities(Index, LastValueSent);
641 LastStateSent.Fill(
false);
642 LastValueSent.Fill((byte)0);
647 public PacLed64Unit(
int Id)
651 PDSingleton = PacDriveSingleton.Instance;
652 PDSingleton.OnPacAttached +=
new PacDriveSingleton.PacAttachedDelegate(Instance_OnPacAttached);
653 PDSingleton.OnPacRemoved +=
new PacDriveSingleton.PacRemovedDelegate(Instance_OnPacRemoved);
655 this.Index = PacDriveSingleton.Instance.PacLed64GetIndexForDeviceId(Id);
657 NewValue.Fill((byte)0);