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];
228 PacLed64Units =
new Dictionary<int, PacLed64Unit>();
229 for (
int i = 1; i <= 4; i++)
231 PacLed64Units.Add(i,
new PacLed64Unit(i));
263 #region Internal class for PacLed64 output states and update methods
265 private static Dictionary<int, PacLed64Unit> PacLed64Units =
new Dictionary<int, PacLed64Unit>();
267 private class PacLed64Unit
275 private const int MaxUpdateFailCount = 5;
278 public int Id {
get;
private set; }
280 private int Index {
get;
set; }
282 private PacDriveSingleton PDSingleton;
284 private byte[] NewValue =
new byte[64];
285 private byte[] CurrentValue =
new byte[64];
288 private byte[] LastValueSent =
new byte[64];
289 private bool[] LastStateSent =
new bool[64];
291 public bool UpdateRequired =
true;
293 public object PacLed64UpdateLocker =
new object();
294 public object ValueChangeLocker =
new object();
296 public Thread PacLed64Updater;
297 public bool KeepPacLed64UpdaterAlive =
false;
298 public object PacLed64UpdaterThreadLocker =
new object();
306 UpdateTimeStatistics =
new TimeSpanStatisticsItem() { Name =
"PacLed64 {0:0} update calls".Build(Id), GroupName =
"OutputControllers - PacLed64" };
311 UpdateTimeStatistics = Pinball.TimeSpanStatistics[
"PacLed64 {0:0} update calls".Build(Id)];
313 if (!Pinball.TimeSpanStatistics.Contains(
"PacLed64 {0:0} PWM updates".Build(Id)))
315 PWMUpdateTimeStatistics =
new TimeSpanStatisticsItem() { Name =
"PacLed64 {0:0} PWM updates".Build(Id), GroupName =
"OutputControllers - PacLed64" };
316 Pinball.TimeSpanStatistics.Add(PWMUpdateTimeStatistics);
320 PWMUpdateTimeStatistics = Pinball.TimeSpanStatistics[
"PacLed64 {0:0} PWM updates".Build(Id)];
322 if (!Pinball.TimeSpanStatistics.Contains(
"PacLed64 {0:0} OnOff updates".Build(Id)))
324 OnOffUpdateTimeStatistics =
new TimeSpanStatisticsItem() { Name =
"PacLed64 {0:0} OnOff updates".Build(Id), GroupName =
"OutputControllers - PacLed64" };
325 Pinball.TimeSpanStatistics.Add(OnOffUpdateTimeStatistics);
329 OnOffUpdateTimeStatistics = Pinball.TimeSpanStatistics[
"PacLed64 {0:0} OnOff updates".Build(Id)];
331 StartPacLed64UpdaterThread();
337 TerminatePacLed64UpdaterThread();
340 UpdateTimeStatistics = null;
341 PWMUpdateTimeStatistics = null;
344 public void UpdateValue(OutputNumbered OutputNumbered)
347 if (!OutputNumbered.Number.IsBetween(1, 64))
return;
349 int ZeroBasedOutputNumber = OutputNumbered.Number - 1;
350 lock (ValueChangeLocker)
352 if (NewValue[ZeroBasedOutputNumber] != OutputNumbered.Value)
354 NewValue[ZeroBasedOutputNumber] = OutputNumbered.Value;
355 UpdateRequired =
true;
360 private void CopyNewToCurrent()
362 lock (ValueChangeLocker)
364 Array.Copy(NewValue, CurrentValue, NewValue.Length);
369 public bool IsUpdaterThreadAlive
373 if (PacLed64Updater != null)
375 return PacLed64Updater.IsAlive;
381 public void StartPacLed64UpdaterThread()
383 lock (PacLed64UpdaterThreadLocker)
385 if (!IsUpdaterThreadAlive)
387 KeepPacLed64UpdaterAlive =
true;
388 PacLed64Updater =
new Thread(PacLed64UpdaterDoIt);
389 PacLed64Updater.Name =
"PacLed64 {0:0} updater thread".Build(Id);
390 PacLed64Updater.Start();
394 public void TerminatePacLed64UpdaterThread()
396 lock (PacLed64UpdaterThreadLocker)
398 if (PacLed64Updater != null)
402 KeepPacLed64UpdaterAlive =
false;
403 lock (PacLed64Updater)
405 Monitor.Pulse(PacLed64Updater);
407 if (!PacLed64Updater.Join(1000))
409 PacLed64Updater.Abort();
415 Log.Exception(
"A error occurd during termination of {0}.".Build(PacLed64Updater.Name), E);
416 throw new Exception(
"A error occurd during termination of {0}.".Build(PacLed64Updater.Name), E);
418 PacLed64Updater = null;
423 bool TriggerUpdate =
false;
424 public void TriggerPacLed64UpdaterThread()
426 TriggerUpdate =
true;
427 lock (PacLed64UpdaterThreadLocker)
429 Monitor.Pulse(PacLed64UpdaterThreadLocker);
435 private void PacLed64UpdaterDoIt()
437 Pinball.ThreadInfoList.HeartBeat(
"PacLed64 {0:0}".Build(Id));
441 while (KeepPacLed64UpdaterAlive)
447 UpdateTimeStatistics.MeasurementStart();
448 SendPacLed64Update();
449 UpdateTimeStatistics.MeasurementStop();
455 Log.Exception(
"A error occured when updating PacLed64 {0}".Build(Id), E);
456 Pinball.ThreadInfoList.RecordException(E);
459 if (FailCnt > MaxUpdateFailCount)
461 Log.Exception(
"More than {0} consecutive updates failed for PacLed64 {1}. Updater thread will terminate.".Build(MaxUpdateFailCount, Id));
462 KeepPacLed64UpdaterAlive =
false;
465 Pinball.ThreadInfoList.HeartBeat();
466 if (KeepPacLed64UpdaterAlive)
468 lock (PacLed64UpdaterThreadLocker)
470 while (!TriggerUpdate && KeepPacLed64UpdaterAlive)
472 Monitor.Wait(PacLed64UpdaterThreadLocker, 50);
473 Pinball.ThreadInfoList.HeartBeat();
479 TriggerUpdate =
false;
481 Pinball.ThreadInfoList.ThreadTerminates();
486 private bool ForceFullUpdate =
true;
487 private void SendPacLed64Update()
492 lock (PacLed64UpdateLocker)
494 lock (ValueChangeLocker)
496 if (!UpdateRequired && !ForceFullUpdate)
return;
500 UpdateRequired =
false;
503 byte IntensityUpdatesRequired = 0;
504 byte StateUpdatesRequired = 0;
505 if (!ForceFullUpdate)
508 for (
int g = 0; g < 8; g++)
511 bool StateUpdateRequired =
false;
512 for (
int p = 0; p < 8; p++)
515 if (CurrentValue[o] > 0)
517 if (CurrentValue[o] != LastValueSent[o])
519 IntensityUpdatesRequired++;
521 else if (!LastStateSent[o])
523 StateUpdateRequired =
true;
526 else if (LastStateSent[o])
528 StateUpdateRequired =
true;
532 if (StateUpdateRequired) StateUpdatesRequired++;
533 StateUpdateRequired =
false;
536 if (ForceFullUpdate || (IntensityUpdatesRequired + StateUpdatesRequired) > 30)
539 PDSingleton.PacLed64SetLEDIntensities(Index, CurrentValue);
540 Array.Copy(CurrentValue, LastValueSent, CurrentValue.Length);
541 for (
int i = 0; i < 64; i++)
543 LastStateSent[i] = (LastValueSent[i] > 0);
549 for (
int g = 0; g < 8; g++)
552 bool StateUpdateRequired =
false;
553 for (
int p = 0; p < 8; p++)
556 if (CurrentValue[o] > 0)
558 if (CurrentValue[o] != LastValueSent[o])
560 PDSingleton.PacLed64SetLEDIntensity(Index, o, CurrentValue[o]);
561 LastStateSent[o] =
true;
562 LastValueSent[o] = CurrentValue[o];
564 else if (!LastStateSent[o])
567 StateUpdateRequired =
true;
568 LastStateSent[o] =
true;
571 else if (LastStateSent[o])
573 StateUpdateRequired =
true;
574 LastStateSent[o] =
false;
578 if (StateUpdateRequired)
580 PDSingleton.PacLed64SetLEDStates(Index, g + 1, (byte)Mask);
581 StateUpdateRequired =
false;
589 ForceFullUpdate =
false;
593 ForceFullUpdate=
true;
598 public void ShutdownLighting()
600 PDSingleton.PacLed64SetLEDStates(0, 0, 0);
601 LastStateSent.Fill(
false);
607 private bool IsPresent
611 if (!Id.IsBetween(1, 4))
return false;
617 void Instance_OnPacRemoved(
int Index)
619 this.Index = PDSingleton.PacLed64GetIndexForDeviceId(Id);
622 void Instance_OnPacAttached(
int Index)
624 this.Index = PDSingleton.PacLed64GetIndexForDeviceId(Id);
626 TriggerPacLed64UpdaterThread();
629 private void InitUnit()
631 LastValueSent.Fill((byte)0);
632 PDSingleton.PacLed64SetLEDIntensities(Index, LastValueSent);
633 LastStateSent.Fill(
false);
634 LastValueSent.Fill((byte)0);
638 public PacLed64Unit(
int Id)
641 PDSingleton = PacDriveSingleton.Instance;
642 PDSingleton.OnPacAttached +=
new PacDriveSingleton.PacAttachedDelegate(Instance_OnPacAttached);
643 PDSingleton.OnPacRemoved +=
new PacDriveSingleton.PacRemovedDelegate(Instance_OnPacRemoved);
644 this.Index = PacDriveSingleton.Instance.PacLed64GetIndexForDeviceId(Id);
645 NewValue.Fill((byte)0);