diff --git a/PanoptoRRLightService/Kuando/BusylightSDK.dll b/PanoptoRRLightService/Kuando/BusylightSDK.dll new file mode 100644 index 0000000..5ed32ce Binary files /dev/null and b/PanoptoRRLightService/Kuando/BusylightSDK.dll differ diff --git a/PanoptoRRLightService/Kuando/KuandoLight.cs b/PanoptoRRLightService/Kuando/KuandoLight.cs new file mode 100644 index 0000000..5dbd5c5 --- /dev/null +++ b/PanoptoRRLightService/Kuando/KuandoLight.cs @@ -0,0 +1,231 @@ +using System; +using System.Threading; +using System.Diagnostics; +using Busylight; + +namespace RRLightProgram +{ + class KuandoLight : ILightControl + { + #region variables + + /// + /// Interface that can post events to a state machine. + /// Does nothing because Kuando Busylight has not input parameters + /// + private IStateMachine stateMachine; + + /// + /// Controls light controls + /// + private KuandoLightWrapper wrapper; + + /// + /// Event indicating the background threads should stop + /// + private ManualResetEvent stopRequested = new ManualResetEvent(initialState: false); + + /// + /// Internal structure to carry the light control request. + /// + struct LightControlRequest + { + public LightColor Color; + public bool Flash; + }; + + /// + /// Latest request of light control. Older pending request is disposed by overwrite. + /// + private LightControlRequest? outstandingRequest = null; + + /// + /// Lock to access outstandingRequest variable. + /// + private object outstandingRequestLock = new object(); + + /// + /// Event to be fired when a request is queued. + /// + private ManualResetEvent outstandingRequestExist = new ManualResetEvent(initialState: false); + + /// + /// Background thread to process light control requests. + /// + private Thread processLightControlRequestsThread; + + #endregion + + #region Constructor, Initialize + + /// + /// Constructor + /// + /// The state machine + public KuandoLight(IStateMachine stateMachine) + { + if (stateMachine == null) + { + throw new ArgumentException("stateMachine cannot be null."); + } + this.stateMachine = stateMachine; + } + + /// + /// Start the kuando light service + /// + /// Whether the service was started successfully + public bool Start() + { + try + { + this.wrapper = new KuandoLightWrapper(); + } + catch (ApplicationException e) + { + Trace.TraceError("Failed to initialize KuandoLightWrapper. {0}", e); + return false; + } + + this.processLightControlRequestsThread = new Thread(ProcessLightControlRequestsLoop); + this.processLightControlRequestsThread.Start(); + + Trace.TraceInformation("started kuando light"); + return true; + } + + /// + /// Stop the Kuando light service + /// + public void Stop() + { + this.stopRequested.Set(); + this.processLightControlRequestsThread.Join(); + + this.wrapper.Close(); + } + + #endregion + + + #region Light Control Logic + + /// + /// Continue in a thread while the service is running and check for new LightControlRequests + /// + private void ProcessLightControlRequestsLoop() + { + while(true) + { + int indexFired = WaitHandle.WaitAny(new WaitHandle[] { this.stopRequested, this.outstandingRequestExist }); + + if (indexFired == 0) + { + // this.stopRequested fired. Exit the thread. + break; + } + + LightControlRequest? request = null; + lock (this.outstandingRequestLock) + { + request = this.outstandingRequest; + this.outstandingRequest = null; + this.outstandingRequestExist.Reset(); + } + + if (!request.HasValue) + { + // Phantom event. Wait next. + continue; + } + Trace.TraceInformation("ProcessLightControlRequestsLoop processing: color={0}, flash={1}", request.Value.Color, request.Value.Flash); + + BusylightColor color = ConvertColor(request.Value.Color); + + if (request.Value.Flash) + { + wrapper.SetFlash(color); + } + else + { + wrapper.SetSolidLight(color); + } + + } + } + + /// + /// Convert a LightColor to a BusylightColor, which can be recognized by Kuando devices + /// + /// The LightColor input + /// The BusyLightColor corresponding to the input + private BusylightColor ConvertColor(LightColor inputColor) + { + BusylightColor result; + + switch (inputColor) + { + case LightColor.Red: + result = BusylightColor.Red; + break; + case LightColor.Green: + result = BusylightColor.Green; + break; + case LightColor.Yellow: + result = BusylightColor.Yellow; + break; + case LightColor.Off: + result = BusylightColor.Off; + break; + default: + throw new ArgumentException("Unexpected inputColor"); + } + + return result; + } + + #endregion + + #region ILightControl + + /// + /// Create a new request for the Kuando light to flash a given color. + /// + /// The color to flash on the light + public void SetFlash(LightColor color) + { + LightControlRequest request = new LightControlRequest() + { + Color = color, + Flash = true + }; + lock (this.outstandingRequestLock) + { + outstandingRequest = request; + outstandingRequestExist.Set(); + TraceVerbose.Trace("SetFlash({0}): request queued.", color); + } + } + + /// + /// Set the Kuando light to be a solid color + /// + /// The color to set the light to + public void SetSolid(LightColor color) + { + LightControlRequest request = new LightControlRequest() + { + Color = color, + Flash = false + }; + lock(this.outstandingRequestLock) + { + outstandingRequest = request; + outstandingRequestExist.Set(); + TraceVerbose.Trace("SetSolid({0}): request queued.", color); + } + } + + #endregion + } +} diff --git a/PanoptoRRLightService/Kuando/KuandoLightWrapper.cs b/PanoptoRRLightService/Kuando/KuandoLightWrapper.cs new file mode 100644 index 0000000..4a47788 --- /dev/null +++ b/PanoptoRRLightService/Kuando/KuandoLightWrapper.cs @@ -0,0 +1,82 @@ +using Busylight; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RRLightProgram +{ + class KuandoLightWrapper + { + #region variables + /// + /// Kuando busylight object. Controls turning light on/off and color + /// + private Busylight.SDK busylight; + + #endregion + + #region Device Setup + + /// + /// Constructor + /// + public KuandoLightWrapper() + { + this.busylight = new Busylight.SDK(); + this.TurnOffAllLights(); + } + + /// + /// Close the wrapper and turn off lights + /// + public void Close() + { + this.TurnOffAllLights(); + + this.CloseDevice(); + } + + /// + /// Close Kuando device + /// + private void CloseDevice() + { + busylight.Terminate(); + } + + /// + /// Turn off the connected lights + /// + private void TurnOffAllLights() + { + busylight.Light(BusylightColor.Off); + } + + #endregion + + #region Light control + + /// + /// Turn on the light as a solid color + /// + /// The color to make the light + public void SetSolidLight(BusylightColor color) + { + this.busylight.Light(color); + } + + /// + /// Set the light to flash a given color + /// + /// The color to flash on the light + public void SetFlash(BusylightColor color) + { + //Flash the light with the given color at 500 ms intervals (1Hz) + this.busylight.Blink(color, 5, 5); + } + + #endregion + } +} diff --git a/PanoptoRRLightService/Properties/AssemblyInfo.cs b/PanoptoRRLightService/Properties/AssemblyInfo.cs index ee41a94..aad6eb9 100644 --- a/PanoptoRRLightService/Properties/AssemblyInfo.cs +++ b/PanoptoRRLightService/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.4.2.0")] -[assembly: AssemblyFileVersion("2.4.2.0")] +[assembly: AssemblyVersion("2.5.0")] +[assembly: AssemblyFileVersion("2.5.0")] diff --git a/PanoptoRRLightService/RRLightService.cs b/PanoptoRRLightService/RRLightService.cs index fe90e6a..37f70f5 100644 --- a/PanoptoRRLightService/RRLightService.cs +++ b/PanoptoRRLightService/RRLightService.cs @@ -22,6 +22,7 @@ public partial class RRLightService : ServiceBase private DelcomLight delcomLight = null; private SwivlChicoLight chicoLight = null; private SerialComm serialComm = null; + private KuandoLight kuandoLight = null; /// /// Constrocutor. @@ -54,7 +55,6 @@ protected override void OnStart(string[] args) this.stateMachine = new StateMachine(); this.remoteRecorderSync = new RemoteRecorderSync((IStateMachine)this.stateMachine); - if (string.Equals(Properties.Settings.Default.DeviceType, "Delcom", StringComparison.OrdinalIgnoreCase)) { // Set up of Delcom light (with button) device. @@ -107,6 +107,20 @@ protected override void OnStart(string[] args) throw new ApplicationException("Failed to start up Serial component. Terminate."); } } + else if (string.Equals(Properties.Settings.Default.DeviceType, "Kuando", StringComparison.OrdinalIgnoreCase)) + { + this.kuandoLight = new KuandoLight((IStateMachine)this.stateMachine); + lightControl = this.kuandoLight as ILightControl; + + if (this.kuandoLight.Start()) + { + Trace.TraceInformation("Kuando Busylight service started"); + } + else + { + Trace.TraceError("Failed to start up Kuando component. Service continues to run without support for the device"); + } + } // TODO: add here for device specific start up when another device type is added. else { @@ -115,6 +129,7 @@ protected override void OnStart(string[] args) // Start processing of the state machine. this.stateMachine.Start(this.remoteRecorderSync, lightControl, resultReceiver); + Trace.TraceInformation("Started program"); } /// @@ -146,6 +161,12 @@ protected override void OnStop() this.serialComm = null; } + if (this.kuandoLight != null) + { + this.kuandoLight.Stop(); + this.kuandoLight = null; + } + if (this.stateMachine != null) { this.stateMachine.Stop(); diff --git a/PanoptoRRLightService/RRLightService.csproj b/PanoptoRRLightService/RRLightService.csproj index edbb1bb..7549033 100644 --- a/PanoptoRRLightService/RRLightService.csproj +++ b/PanoptoRRLightService/RRLightService.csproj @@ -51,6 +51,10 @@ true + + False + Kuando\BusylightSDK.dll + False \\tfs\tfsbuilds\pre_prod_x86_Official\pre_prod_x86_Official_20151216.1\RemoteRecorderAPI.dll @@ -74,6 +78,8 @@ + + Component @@ -149,6 +155,9 @@ chicntrl.dll + + + diff --git a/PanoptoRRLightServiceInstaller/CustomWelcomeDlg.wxs b/PanoptoRRLightServiceInstaller/CustomWelcomeDlg.wxs index 6f8ae46..ca1efee 100644 --- a/PanoptoRRLightServiceInstaller/CustomWelcomeDlg.wxs +++ b/PanoptoRRLightServiceInstaller/CustomWelcomeDlg.wxs @@ -9,6 +9,7 @@ + diff --git a/PanoptoRRLightServiceInstaller/Product.wxs b/PanoptoRRLightServiceInstaller/Product.wxs index 3e2f4dd..0daa021 100644 --- a/PanoptoRRLightServiceInstaller/Product.wxs +++ b/PanoptoRRLightServiceInstaller/Product.wxs @@ -1,7 +1,7 @@ - + @@ -44,6 +44,9 @@ + + +