2 using System.Collections.Generic;
5 using System.Xml.Serialization;
18 byte[] OutputValues =
new byte[0];
29 int NumberOfOutputs = GetNumberOfConfiguredOutputs().Limit(0,
int.MaxValue);
35 Outputs.Where(O => O.Number > NumberOfOutputs).ToList().ForEach(ODEL => Outputs.Remove(ODEL));
37 for (
int i = 1; i <= NumberOfOutputs; i++)
39 if (!Outputs.Any(O => O.Number == i))
41 Outputs.Add(
new Output() { Name =
"{0}.{1}".Build((Name != null ? Name :
""), i), Number = i, Value = 0 });
46 OutputValues = Outputs.OrderBy(O => O.Number).Select(O => O.Value).ToArray();
49 private void RenameOutputs()
55 O.
Name =
"{0}.{1}".Build((Name != null ? Name :
""), O.
Name);
63 base.AfterNameChange(OldName, NewName);
75 get {
return _Outputs; }
94 private void Outputs_OutputValueChanged(
object sender,
OutputEventArgs e)
96 OnOutputValueChanged(e.
Output);
104 private void OnOutputValueChanged(IOutput Output)
106 if (Output.Number > 0 && Output.Number <= OutputValues.Length)
108 lock (ValueChangeLocker)
110 if (OutputValues[Output.Number - 1] != Output.Value)
112 OutputValues[Output.Number - 1] = Output.Value;
113 UpdateRequired =
true;
122 #region ISupportsSetValues Member
131 if (FirstOutput >= OutputValues.Length)
return;
132 if (FirstOutput < 0)
return;
133 int CopyLength = (OutputValues.Length - FirstOutput).Limit(0, Values.Length);
134 if (CopyLength < 1)
return;
136 lock (ValueChangeLocker)
138 Buffer.BlockCopy(Values, 0, OutputValues, FirstOutput, CopyLength);
139 UpdateRequired =
true;
156 V = VerifySettings();
160 string msg =
"A exception occured when verifying the settings for {0} {1}: {2}. Cant initialize.".Build(this.GetType().Name, Name, E.Message);
162 throw new Exception(msg, E);
168 Log.
Write(
"{0} {1} intialized and updater thread started.".Build(this.GetType().Name, Name));
172 Log.
Warning(
"Settings for {0} {1} are not correct. Cant initialize.".Build(this.GetType().Name, Name));
182 FinishUpdaterThread();
183 Log.
Write(
"{0} {1} finished and updater thread stopped.".Build(this.GetType().Name, Name));
193 UpdaterThreadSignal();
204 protected abstract int GetNumberOfConfiguredOutputs();
210 protected abstract bool VerifySettings();
217 protected abstract void UpdateOutputs(byte[] OutputValues);
225 protected abstract void ConnectToController();
232 protected abstract void DisconnectFromController();
235 #region UpdaterThread
236 private void InitUpdaterThread()
243 if (!UpdaterThreadIsActive)
245 KeepUpdaterThreadAlive =
true;
248 UpdaterThread =
new Thread(UpdaterThreadDoIt);
249 UpdaterThread.Name =
"{0} {1} updater thread ".Build(this.GetType().Name, Name);
250 UpdaterThread.Start();
254 Log.
Exception(
"{0} {1} updater thread could not start.".Build(this.GetType().Name, Name), E);
255 throw new Exception(
"{0} {1} updater thread could not start.".Build(this.GetType().Name, Name), E);
264 private void FinishUpdaterThread()
266 if (UpdaterThread != null)
270 KeepUpdaterThreadAlive =
false;
271 UpdaterThreadSignal();
272 if (!UpdaterThread.Join(1000))
274 UpdaterThread.Abort();
275 Log.Warning(
"{0} did not quit. Forcing abort.".Build(UpdaterThread.Name));
277 UpdaterThread = null;
281 Log.Exception(
"A error occured during termination of the {0}: {1}.".Build(UpdaterThread.Name), E);
282 throw new Exception(
"A error occured during termination of the {0}: {1}.".Build(UpdaterThread.Name), E);
291 public bool UpdaterThreadIsActive
295 if (UpdaterThread != null)
297 if (UpdaterThread.IsAlive)
309 private void UpdaterThreadSignal()
311 lock (UpdaterThreadLocker)
313 Monitor.Pulse(UpdaterThreadLocker);
318 private object ValueChangeLocker =
new object();
320 private Thread UpdaterThread {
get; set; }
321 private object UpdaterThreadLocker =
new object();
322 private bool KeepUpdaterThreadAlive =
true;
324 private bool UpdateRequired =
false;
326 private void UpdaterThreadDoIt()
328 Log.Write(
"{0} started.".Build(Thread.CurrentThread.Name));
333 ConnectToController();
338 Log.Exception(
"{0} could not connect to the controller. Thread will quit.".Build(Thread.CurrentThread.Name), E);
342 DisconnectFromController();
348 Log.Write(
"{0} has connected to {1} {2}.".Build(Thread.CurrentThread.Name,
this.GetType().Name, Name));
350 while (KeepUpdaterThreadAlive)
352 byte[] ValuesToSend =
new byte[0];
353 lock (ValueChangeLocker)
355 ValuesToSend = (byte[])OutputValues.Clone();
359 bool UpdateOK =
true;
360 for (
int i = 0; i < 1; i++)
365 UpdateOutputs(ValuesToSend);
369 Log.Exception(
"{0} could not send update for {1} {2}: {3}. Will try again.".Build(
new object[] { Thread.CurrentThread.Name, this.GetType().Name, Name, E.Message }), E);
377 Log.Warning(
"{0} tries to reconnect to {1} {2}.".Build(Thread.CurrentThread.Name,
this.GetType().Name, Name));
380 DisconnectFromController();
386 ConnectToController();
391 Log.Exception(
"{0} could not reconnect to the controller. Thread will quit.".Build(Thread.CurrentThread.Name), E);
395 DisconnectFromController();
400 Log.Write(
"{0} has reconnected to {1} {2}.".Build(Thread.CurrentThread.Name,
this.GetType().Name, Name));
404 UpdateOutputs(ValuesToSend);
408 Log.Exception(
"{0} could still not send update for {1} {2}: {3}. Thread will quit.".Build(
new object[] { Thread.CurrentThread.Name, this.GetType().Name, Name, E.Message }), E);
411 DisconnectFromController();
421 if (KeepUpdaterThreadAlive)
423 lock (UpdaterThreadLocker)
425 while (!UpdateRequired && KeepUpdaterThreadAlive)
427 Monitor.Wait(UpdaterThreadLocker, 50);
432 UpdateRequired =
false;
437 UpdateOutputs(
new byte[OutputValues.Length]);
441 Log.Exception(
"A exception occured in {0} while trying to turn of all outputs for {1} {2}".Build(Thread.CurrentThread.Name,
this.GetType().Name, Name), E);
445 DisconnectFromController();
450 Log.Write(
"{0} has disconnected from {1} {2} and will terminate.".Build(Thread.CurrentThread.Name,
this.GetType().Name, Name));
454 Log.Exception(
"A exception has occured in {0}. Thread will quit. Message: {1}".Build(Thread.CurrentThread.Name, EU.Message), EU);
IOutput Output
IOutput objec^t which has triggered the event.
The Cabinet object describes the parts of a pinball cabinet (toys, outputcontrollers, outputs and more).
void Update()
Triggers the update of the physical outputs
Abstract base class for named items. Implements the name property and the necessary events...
OutputValueChangedEventHandler OutputValueChanged
Event fires if the value of any IOutput in the list has changed.
static void Warning(string Message)
Writes a warning message to the log.
string Name
Gets or sets the name of the item. Must fire the BeforeNameChange and AfterNameChange events when th...
virtual void Finish()
Finishes the output controller and stop the updater thread.
override void AfterNameChange(string OldName, string NewName)
static void Write(string Message)
Writes the specified message to the logfile.
A simple logger used to record important events and exceptions.
This abstract class implement the full base logic for a output controller with a separate thread for ...
Common interface for outputs of any output controller. The Output class implements this interface and...
void SetupOutputs()
Manages to output object of the output controller. Use the GetNumberOfConfiguredOutputs() method to d...
Common interface for all outputcontrollers. Only classes implementing this interface can be used as o...
EventArgs for events of IOutput objects.
This interface defines additional methods for output controllers which allow for direct modification ...
void SetValues(int FirstOutput, byte[] Values)
Sets the values for one or several outputs of the controller.
virtual void Init(Cabinet Cabinet)
Initializes the output controller and starts the updater thread.
delegate void OutputValueChangedEventHandler(object sender, OutputEventArgs e)
Handler for the OutputValueChangedEvent.
The namespace DirectOutput.General contains classes for general use.
static void Exception(string Message, Exception E=null)
Writes a exception message to the log.
Basic IOutput implementation.