Merge pull request #1345 from PepperDash/mc-touchpanel-key

mc touchpanel key
This commit is contained in:
Andrew Welker
2025-10-30 17:11:15 -04:00
committed by GitHub
18 changed files with 994 additions and 289 deletions

1
.gitignore vendored
View File

@@ -396,3 +396,4 @@ _site/
api/ api/
*.DS_Store *.DS_Store
/._PepperDash.Essentials.4Series.sln /._PepperDash.Essentials.4Series.sln
dotnet

View File

@@ -1,6 +1,6 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Version>2.18.2-local</Version> <Version>2.19.4-local</Version>
<InformationalVersion>$(Version)</InformationalVersion> <InformationalVersion>$(Version)</InformationalVersion>
<Authors>PepperDash Technology</Authors> <Authors>PepperDash Technology</Authors>
<Company>PepperDash Technology</Company> <Company>PepperDash Technology</Company>

View File

@@ -1,59 +1,76 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro; using Crestron.SimplSharpPro;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Core.Logging;
using Serilog.Events; using Serilog.Events;
namespace PepperDash.Essentials.Core namespace PepperDash.Essentials.Core
{ {
/// <summary> /// <summary>
/// Represents a ComPortController /// Represents a ComPortController
/// </summary> /// </summary>
public class ComPortController : Device, IBasicCommunicationWithStreamDebugging public class ComPortController : Device, IBasicCommunicationWithStreamDebugging
{ {
/// <summary> /// <summary>
/// Gets or sets the StreamDebugging /// Gets or sets the StreamDebugging
/// </summary> /// </summary>
public CommunicationStreamDebugging StreamDebugging { get; private set; } public CommunicationStreamDebugging StreamDebugging { get; private set; }
/// <summary>
/// Event fired when bytes are received
/// </summary>
public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived; public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
/// <summary>
/// Event fired when text is received
/// </summary>
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived; public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
/// <summary> /// <summary>
/// Gets or sets the IsConnected /// Gets or sets the IsConnected
/// </summary> /// </summary>
public bool IsConnected { get { return true; } } public bool IsConnected { get { return true; } }
ComPort Port; ComPort Port;
ComPort.ComPortSpec Spec; ComPort.ComPortSpec Spec;
public ComPortController(string key, Func<EssentialsControlPropertiesConfig, ComPort> postActivationFunc, /// <summary>
ComPort.ComPortSpec spec, EssentialsControlPropertiesConfig config) : base(key) /// Constructor
{ /// </summary>
StreamDebugging = new CommunicationStreamDebugging(key); /// <param name="key"></param>
/// <param name="postActivationFunc"></param>
/// <param name="spec"></param>
/// <param name="config"></param>
public ComPortController(string key, Func<EssentialsControlPropertiesConfig, ComPort> postActivationFunc,
ComPort.ComPortSpec spec, EssentialsControlPropertiesConfig config) : base(key)
{
StreamDebugging = new CommunicationStreamDebugging(key);
Spec = spec; Spec = spec;
AddPostActivationAction(() => AddPostActivationAction(() =>
{ {
Port = postActivationFunc(config); Port = postActivationFunc(config);
RegisterAndConfigureComPort(); RegisterAndConfigureComPort();
}); });
} }
/// <summary>
/// Constructor
/// </summary>
/// <param name="key">Device key</param>
/// <param name="port">COM port instance</param>
/// <param name="spec">COM port specification</param>
public ComPortController(string key, ComPort port, ComPort.ComPortSpec spec) public ComPortController(string key, ComPort port, ComPort.ComPortSpec spec)
: base(key) : base(key)
{ {
if (port == null) if (port == null)
{ {
Debug.LogMessage(LogEventLevel.Information, this, "ERROR: Invalid com port, continuing but comms will not function"); Debug.LogMessage(LogEventLevel.Information, this, "ERROR: Invalid com port, continuing but comms will not function");
return; return;
} }
@@ -64,71 +81,111 @@ namespace PepperDash.Essentials.Core
RegisterAndConfigureComPort(); RegisterAndConfigureComPort();
} }
private void RegisterAndConfigureComPort() private void RegisterAndConfigureComPort()
{ {
if (Port == null) if (Port == null)
{ {
Debug.LogMessage(LogEventLevel.Information, this, "Configured com Port for this device does not exist."); Debug.LogMessage(LogEventLevel.Information, this, "Configured com Port for this device does not exist.");
return; return;
} }
if (Port.Parent is CrestronControlSystem) // TODO [ ] - Remove commented out code once verified working
{ //if (Port.Parent is CrestronControlSystem || Port.Parent is CenIoCom102)
var result = Port.Register(); if (Port.Parent is GenericBase genericDevice && genericDevice.Registerable)
if (result != eDeviceRegistrationUnRegistrationResponse.Success) {
{ //this.LogInformation($"INFO: Attempting to register {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID}");
Debug.LogMessage(LogEventLevel.Information, this, "ERROR: Cannot register Com port: {0}", result); var result = genericDevice.Register();
return; // false if (result != eDeviceRegistrationUnRegistrationResponse.Success)
} {
} this.LogError($"ERROR: Cannot register {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID} (result == {result})");
return; // false
}
}
var specResult = Port.SetComPortSpec(Spec);
if (specResult != 0)
{
this.LogError($"ERROR: Cannot set comspec for {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID} (result == {specResult})");
return;
}
//this.LogInformation($"INFO: Successfully set comspec for {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID} (result == {specResult})");
var specResult = Port.SetComPortSpec(Spec);
if (specResult != 0)
{
Debug.LogMessage(LogEventLevel.Information, this, "WARNING: Cannot set comspec");
return;
}
Port.SerialDataReceived += Port_SerialDataReceived;
}
~ComPortController() // TODO [ ] - Remove debug logging once verified working
// if (Port.Parent is CenIoCom102)
// {
// Port.PropertyChanged += (s, e) =>
// {
// this.LogInformation($@"RegisterAndConfigureComPort: PropertyChanged Fired >>
// comPort-'{Port.ID}',
// Property Changed-'{e.Property}',
// Value Changed-'{e.Value}',
// deviceName-'{Port.DeviceName}',
// parentDevice-'{Port.ParentDevice}',
// parent-`{Port.Parent}`,
// online-`{Port.IsOnline}`,
// present-`{Port.Present}`,
// supportedBaudRates-'{Port.SupportedBaudRates}'");
// };
// Port.ExtendedInformationChanged += (s, e) =>
// {
// this.LogInformation($@"RegisterAndConfigureComPort: ExtendedInformationChanged Fired >>
// comPort-'{Port.ID}',
// {e.Protocol},
// {e.BaudRate},
// {e.Parity},
// {e.DataBits},
// {e.StopBits},
// HW Handshake-'{e.HardwareHandshakeSetting}',
// SW Handshake-'{e.SoftwareHandshakeSetting}'");
// };
// }
Port.SerialDataReceived += Port_SerialDataReceived;
}
/// <summary>
/// Destructor
/// </summary>
~ComPortController()
{ {
Port.SerialDataReceived -= Port_SerialDataReceived; Port.SerialDataReceived -= Port_SerialDataReceived;
} }
void Port_SerialDataReceived(ComPort ReceivingComPort, ComPortSerialDataEventArgs args) void Port_SerialDataReceived(ComPort ReceivingComPort, ComPortSerialDataEventArgs args)
{ {
OnDataReceived(args.SerialData); OnDataReceived(args.SerialData);
} }
void OnDataReceived(string s) void OnDataReceived(string s)
{ {
var eventSubscribed = false; var eventSubscribed = false;
var bytesHandler = BytesReceived; var bytesHandler = BytesReceived;
if (bytesHandler != null) if (bytesHandler != null)
{ {
var bytes = Encoding.GetEncoding(28591).GetBytes(s); var bytes = Encoding.GetEncoding(28591).GetBytes(s);
if (StreamDebugging.RxStreamDebuggingIsEnabled) if (StreamDebugging.RxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", ComTextHelper.GetEscapedText(bytes)); Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", ComTextHelper.GetEscapedText(bytes));
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes)); bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
eventSubscribed = true; eventSubscribed = true;
} }
var textHandler = TextReceived; var textHandler = TextReceived;
if (textHandler != null) if (textHandler != null)
{ {
if (StreamDebugging.RxStreamDebuggingIsEnabled) if (StreamDebugging.RxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", s); Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", s);
textHandler(this, new GenericCommMethodReceiveTextArgs(s)); textHandler(this, new GenericCommMethodReceiveTextArgs(s));
eventSubscribed = true; eventSubscribed = true;
} }
if(!eventSubscribed) Debug.LogMessage(LogEventLevel.Warning, this, "Received data but no handler is registered"); if (!eventSubscribed) Debug.LogMessage(LogEventLevel.Warning, this, "Received data but no handler is registered");
} }
/// <summary> /// <summary>
/// Deactivate method /// Deactivate method
/// </summary> /// </summary>
/// <inheritdoc /> /// <inheritdoc />
public override bool Deactivate() public override bool Deactivate()
{ {
return Port.UnRegister() == eDeviceRegistrationUnRegistrationResponse.Success; return Port.UnRegister() == eDeviceRegistrationUnRegistrationResponse.Success;
@@ -136,70 +193,70 @@ namespace PepperDash.Essentials.Core
#region IBasicCommunication Members #region IBasicCommunication Members
/// <summary> /// <summary>
/// SendText method /// SendText method
/// </summary> /// </summary>
public void SendText(string text) public void SendText(string text)
{ {
if (Port == null) if (Port == null)
return; return;
if (StreamDebugging.TxStreamDebuggingIsEnabled) if (StreamDebugging.TxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} characters of text: '{1}'", text.Length, text); Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} characters of text: '{1}'", text.Length, text);
Port.Send(text); Port.Send(text);
} }
/// <summary> /// <summary>
/// SendBytes method /// SendBytes method
/// </summary> /// </summary>
public void SendBytes(byte[] bytes) public void SendBytes(byte[] bytes)
{ {
if (Port == null) if (Port == null)
return; return;
var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length); var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
if (StreamDebugging.TxStreamDebuggingIsEnabled) if (StreamDebugging.TxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes)); Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
Port.Send(text); Port.Send(text);
} }
/// <summary> /// <summary>
/// Connect method /// Connect method
/// </summary> /// </summary>
public void Connect() public void Connect()
{ {
} }
/// <summary> /// <summary>
/// Disconnect method /// Disconnect method
/// </summary> /// </summary>
public void Disconnect() public void Disconnect()
{ {
} }
#endregion #endregion
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
/// <param name="s"></param> /// <param name="s"></param>
/// <summary> /// <summary>
/// SimulateReceive method /// SimulateReceive method
/// </summary> /// </summary>
public void SimulateReceive(string s) public void SimulateReceive(string s)
{ {
// split out hex chars and build string // split out hex chars and build string
var split = Regex.Split(s, @"(\\[Xx][0-9a-fA-F][0-9a-fA-F])"); var split = Regex.Split(s, @"(\\[Xx][0-9a-fA-F][0-9a-fA-F])");
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
foreach (var t in split) foreach (var t in split)
{ {
if (t.StartsWith(@"\") && t.Length == 4) if (t.StartsWith(@"\") && t.Length == 4)
b.Append((char)(Convert.ToByte(t.Substring(2, 2), 16))); b.Append((char)(Convert.ToByte(t.Substring(2, 2), 16)));
else else
b.Append(t); b.Append(t);
} }
OnDataReceived(b.ToString()); OnDataReceived(b.ToString());
} }
} }
} }

View File

@@ -16,121 +16,201 @@ namespace PepperDash.Essentials.Core.Fusion
{ {
// Processor Attributes // Processor Attributes
/// <summary>
/// Processor IP 1
/// </summary>
[JoinName("ProcessorIp1")] [JoinName("ProcessorIp1")]
public JoinDataComplete ProcessorIp1 = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1, AttributeName = "Info - Processor - IP 1" }, public JoinDataComplete ProcessorIp1 = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1, AttributeName = "Info - Processor - IP 1" },
new JoinMetadata { Description = "Info - Processor - IP 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); new JoinMetadata { Description = "Info - Processor - IP 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Processor IP 2
/// </summary>
[JoinName("ProcessorIp2")] [JoinName("ProcessorIp2")]
public JoinDataComplete ProcessorIp2 = new JoinDataComplete(new JoinData { JoinNumber = 51, JoinSpan = 1, AttributeName = "Info - Processor - IP 2" }, public JoinDataComplete ProcessorIp2 = new JoinDataComplete(new JoinData { JoinNumber = 51, JoinSpan = 1, AttributeName = "Info - Processor - IP 2" },
new JoinMetadata { Description = "Info - Processor - IP 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); new JoinMetadata { Description = "Info - Processor - IP 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Processor Gateway
/// </summary>
[JoinName("ProcessorGateway")] [JoinName("ProcessorGateway")]
public JoinDataComplete ProcessorGateway = new JoinDataComplete(new JoinData { JoinNumber = 52, JoinSpan = 1, AttributeName = "Info - Processor - Gateway" }, public JoinDataComplete ProcessorGateway = new JoinDataComplete(new JoinData { JoinNumber = 52, JoinSpan = 1, AttributeName = "Info - Processor - Gateway" },
new JoinMetadata { Description = "Info - Processor - Gateway", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); new JoinMetadata { Description = "Info - Processor - Gateway", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Processor Hostname
/// </summary>
[JoinName("ProcessorHostname")] [JoinName("ProcessorHostname")]
public JoinDataComplete ProcessorHostname = new JoinDataComplete(new JoinData { JoinNumber = 53, JoinSpan = 1, AttributeName = "Info - Processor - Hostname" }, public JoinDataComplete ProcessorHostname = new JoinDataComplete(new JoinData { JoinNumber = 53, JoinSpan = 1, AttributeName = "Info - Processor - Hostname" },
new JoinMetadata { Description = "Info - Processor - Hostname", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); new JoinMetadata { Description = "Info - Processor - Hostname", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Processor Domain
/// </summary>
[JoinName("ProcessorDomain")] [JoinName("ProcessorDomain")]
public JoinDataComplete ProcessorDomain = new JoinDataComplete(new JoinData { JoinNumber = 54, JoinSpan = 1, AttributeName = "Info - Processor - Domain" }, public JoinDataComplete ProcessorDomain = new JoinDataComplete(new JoinData { JoinNumber = 54, JoinSpan = 1, AttributeName = "Info - Processor - Domain" },
new JoinMetadata { Description = "Info - Processor - Domain", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); new JoinMetadata { Description = "Info - Processor - Domain", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Processor DNS 1
/// </summary>
[JoinName("ProcessorDns1")] [JoinName("ProcessorDns1")]
public JoinDataComplete ProcessorDns1 = new JoinDataComplete(new JoinData { JoinNumber = 55, JoinSpan = 1, AttributeName = "Info - Processor - DNS 1" }, public JoinDataComplete ProcessorDns1 = new JoinDataComplete(new JoinData { JoinNumber = 55, JoinSpan = 1, AttributeName = "Info - Processor - DNS 1" },
new JoinMetadata { Description = "Info - Processor - DNS 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); new JoinMetadata { Description = "Info - Processor - DNS 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Processor DNS 2
/// </summary>
[JoinName("ProcessorDns2")] [JoinName("ProcessorDns2")]
public JoinDataComplete ProcessorDns2 = new JoinDataComplete(new JoinData { JoinNumber = 56, JoinSpan = 1, AttributeName = "Info - Processor - DNS 2" }, public JoinDataComplete ProcessorDns2 = new JoinDataComplete(new JoinData { JoinNumber = 56, JoinSpan = 1, AttributeName = "Info - Processor - DNS 2" },
new JoinMetadata { Description = "Info - Processor - DNS 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); new JoinMetadata { Description = "Info - Processor - DNS 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Processor MAC 1
/// </summary>
[JoinName("ProcessorMac1")] [JoinName("ProcessorMac1")]
public JoinDataComplete ProcessorMac1 = new JoinDataComplete(new JoinData { JoinNumber = 57, JoinSpan = 1, AttributeName = "Info - Processor - MAC 1" }, public JoinDataComplete ProcessorMac1 = new JoinDataComplete(new JoinData { JoinNumber = 57, JoinSpan = 1, AttributeName = "Info - Processor - MAC 1" },
new JoinMetadata { Description = "Info - Processor - MAC 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); new JoinMetadata { Description = "Info - Processor - MAC 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Processor MAC 2
/// </summary>
[JoinName("ProcessorMac2")] [JoinName("ProcessorMac2")]
public JoinDataComplete ProcessorMac2 = new JoinDataComplete(new JoinData { JoinNumber = 58, JoinSpan = 1, AttributeName = "Info - Processor - MAC 2" }, public JoinDataComplete ProcessorMac2 = new JoinDataComplete(new JoinData { JoinNumber = 58, JoinSpan = 1, AttributeName = "Info - Processor - MAC 2" },
new JoinMetadata { Description = "Info - Processor - MAC 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); new JoinMetadata { Description = "Info - Processor - MAC 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Processor Net Mask 1
/// </summary>
[JoinName("ProcessorNetMask1")] [JoinName("ProcessorNetMask1")]
public JoinDataComplete ProcessorNetMask1 = new JoinDataComplete(new JoinData { JoinNumber = 59, JoinSpan = 1, AttributeName = "Info - Processor - Net Mask 1" }, public JoinDataComplete ProcessorNetMask1 = new JoinDataComplete(new JoinData { JoinNumber = 59, JoinSpan = 1, AttributeName = "Info - Processor - Net Mask 1" },
new JoinMetadata { Description = "Info - Processor - Net Mask 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); new JoinMetadata { Description = "Info - Processor - Net Mask 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Processor Net Mask 2
/// </summary>
[JoinName("ProcessorNetMask2")] [JoinName("ProcessorNetMask2")]
public JoinDataComplete ProcessorNetMask2 = new JoinDataComplete(new JoinData { JoinNumber = 60, JoinSpan = 1, AttributeName = "Info - Processor - Net Mask 2" }, public JoinDataComplete ProcessorNetMask2 = new JoinDataComplete(new JoinData { JoinNumber = 60, JoinSpan = 1, AttributeName = "Info - Processor - Net Mask 2" },
new JoinMetadata { Description = "Info - Processor - Net Mask 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); new JoinMetadata { Description = "Info - Processor - Net Mask 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Processor Firmware
/// </summary>
[JoinName("ProcessorFirmware")] [JoinName("ProcessorFirmware")]
public JoinDataComplete ProcessorFirmware = new JoinDataComplete(new JoinData { JoinNumber = 61, JoinSpan = 1, AttributeName = "Info - Processor - Firmware" }, public JoinDataComplete ProcessorFirmware = new JoinDataComplete(new JoinData { JoinNumber = 61, JoinSpan = 1, AttributeName = "Info - Processor - Firmware" },
new JoinMetadata { Description = "Info - Processor - Firmware", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); new JoinMetadata { Description = "Info - Processor - Firmware", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Program Name Start
/// </summary>
[JoinName("ProgramNameStart")] [JoinName("ProgramNameStart")]
public JoinDataComplete ProgramNameStart = new JoinDataComplete(new JoinData { JoinNumber = 62, JoinSpan = 10, AttributeName = "Info - Processor - Program" }, public JoinDataComplete ProgramNameStart = new JoinDataComplete(new JoinData { JoinNumber = 62, JoinSpan = 10, AttributeName = "Info - Processor - Program" },
new JoinMetadata { Description = "Info - Processor - Program", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); new JoinMetadata { Description = "Info - Processor - Program", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Processor Reboot
/// </summary>
[JoinName("ProcessorReboot")] [JoinName("ProcessorReboot")]
public JoinDataComplete ProcessorReboot = new JoinDataComplete(new JoinData { JoinNumber = 74, JoinSpan = 1, AttributeName = "Processor - Reboot" }, public JoinDataComplete ProcessorReboot = new JoinDataComplete(new JoinData { JoinNumber = 74, JoinSpan = 1, AttributeName = "Processor - Reboot" },
new JoinMetadata { Description = "Processor - Reboot", JoinCapabilities = eJoinCapabilities.FromFusion, JoinType = eJoinType.Digital }); new JoinMetadata { Description = "Processor - Reboot", JoinCapabilities = eJoinCapabilities.FromFusion, JoinType = eJoinType.Digital });
// Volume Controls // Volume Controls
/// <summary>
/// Volume Fader 1
/// </summary>
[JoinName("VolumeFader1")] [JoinName("VolumeFader1")]
public JoinDataComplete VolumeFader1 = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1, AttributeName = "Volume - Fader01" }, public JoinDataComplete VolumeFader1 = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1, AttributeName = "Volume - Fader01" },
new JoinMetadata { Description = "Volume - Fader01", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Analog }); new JoinMetadata { Description = "Volume - Fader01", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Analog });
// Codec Info // Codec Info
/// <summary>
/// VC Codec In Call
/// </summary>
[JoinName("VcCodecInCall")] [JoinName("VcCodecInCall")]
public JoinDataComplete VcCodecInCall = new JoinDataComplete(new JoinData { JoinNumber = 69, JoinSpan = 1, AttributeName = "Conf - VC 1 In Call" }, public JoinDataComplete VcCodecInCall = new JoinDataComplete(new JoinData { JoinNumber = 69, JoinSpan = 1, AttributeName = "Conf - VC 1 In Call" },
new JoinMetadata { Description = "Conf - VC 1 In Call", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); new JoinMetadata { Description = "Conf - VC 1 In Call", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital });
/// <summary>
/// VC Codec Online
/// </summary>
[JoinName("VcCodecOnline")] [JoinName("VcCodecOnline")]
public JoinDataComplete VcCodecOnline = new JoinDataComplete(new JoinData { JoinNumber = 122, JoinSpan = 1, AttributeName = "Online - VC 1" }, public JoinDataComplete VcCodecOnline = new JoinDataComplete(new JoinData { JoinNumber = 122, JoinSpan = 1, AttributeName = "Online - VC 1" },
new JoinMetadata { Description = "Online - VC 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); new JoinMetadata { Description = "Online - VC 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital });
/// <summary>
/// VC Codec IP Address
/// </summary>
[JoinName("VcCodecIpAddress")] [JoinName("VcCodecIpAddress")]
public JoinDataComplete VcCodecIpAddress = new JoinDataComplete(new JoinData { JoinNumber = 121, JoinSpan = 1, AttributeName = "IP Address - VC" }, public JoinDataComplete VcCodecIpAddress = new JoinDataComplete(new JoinData { JoinNumber = 121, JoinSpan = 1, AttributeName = "IP Address - VC" },
new JoinMetadata { Description = "IP Address - VC", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); new JoinMetadata { Description = "IP Address - VC", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// VC Codec IP Port
/// </summary>
[JoinName("VcCodecIpPort")] [JoinName("VcCodecIpPort")]
public JoinDataComplete VcCodecIpPort = new JoinDataComplete(new JoinData { JoinNumber = 150, JoinSpan = 1, AttributeName = "IP Port - VC" }, public JoinDataComplete VcCodecIpPort = new JoinDataComplete(new JoinData { JoinNumber = 150, JoinSpan = 1, AttributeName = "IP Port - VC" },
new JoinMetadata { Description = "IP Port - VC", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); new JoinMetadata { Description = "IP Port - VC", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
// Source Attributes // Source Attributes
/// <summary>
/// Display 1 Current Source Name
/// </summary>
[JoinName("Display1CurrentSourceName")] [JoinName("Display1CurrentSourceName")]
public JoinDataComplete Display1CurrentSourceName = new JoinDataComplete(new JoinData { JoinNumber = 84, JoinSpan = 1, AttributeName = "Display 1 - Current Source" }, public JoinDataComplete Display1CurrentSourceName = new JoinDataComplete(new JoinData { JoinNumber = 84, JoinSpan = 1, AttributeName = "Display 1 - Current Source" },
new JoinMetadata { Description = "Display 1 - Current Source", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); new JoinMetadata { Description = "Display 1 - Current Source", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
// Device Online Status // Device Online Status
/// <summary>
/// Touchpanel Online Start
/// </summary>
[JoinName("TouchpanelOnlineStart")] [JoinName("TouchpanelOnlineStart")]
public JoinDataComplete TouchpanelOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 150, JoinSpan = 10, AttributeName = "Online - Touch Panel" }, public JoinDataComplete TouchpanelOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 150, JoinSpan = 10, AttributeName = "Online - Touch Panel" },
new JoinMetadata { Description = "Online - Touch Panel", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); new JoinMetadata { Description = "Online - Touch Panel", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital });
/// <summary>
/// Xpanel Online Start
/// </summary>
[JoinName("XpanelOnlineStart")] [JoinName("XpanelOnlineStart")]
public JoinDataComplete XpanelOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 160, JoinSpan = 5, AttributeName = "Online - XPanel" }, public JoinDataComplete XpanelOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 160, JoinSpan = 5, AttributeName = "Online - XPanel" },
new JoinMetadata { Description = "Online - XPanel", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); new JoinMetadata { Description = "Online - XPanel", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital });
/// <summary>
/// Display Online Start
/// </summary>
[JoinName("DisplayOnlineStart")] [JoinName("DisplayOnlineStart")]
public JoinDataComplete DisplayOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 170, JoinSpan = 10, AttributeName = "Online - Display" }, public JoinDataComplete DisplayOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 170, JoinSpan = 10, AttributeName = "Online - Display" },
new JoinMetadata { Description = "Online - Display", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); new JoinMetadata { Description = "Online - Display", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital });
/// <summary>
/// Display 1 Laptop Source Start
/// </summary>
[JoinName("Display1LaptopSourceStart")] [JoinName("Display1LaptopSourceStart")]
public JoinDataComplete Display1LaptopSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 166, JoinSpan = 5, AttributeName = "Display 1 - Source Laptop" }, public JoinDataComplete Display1LaptopSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 165, JoinSpan = 5, AttributeName = "Display 1 - Source Laptop" },
new JoinMetadata { Description = "Display 1 - Source Laptop", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital }); new JoinMetadata { Description = "Display 1 - Source Laptop", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital });
/// <summary>
/// Display 1 Disc Player Source Start
/// </summary>
[JoinName("Display1DiscPlayerSourceStart")] [JoinName("Display1DiscPlayerSourceStart")]
public JoinDataComplete Display1DiscPlayerSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 181, JoinSpan = 5, AttributeName = "Display 1 - Source Disc Player" }, public JoinDataComplete Display1DiscPlayerSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 180, JoinSpan = 5, AttributeName = "Display 1 - Source Disc Player" },
new JoinMetadata { Description = "Display 1 - Source Disc Player", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital }); new JoinMetadata { Description = "Display 1 - Source Disc Player", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital });
/// <summary>
/// Display 1 Set Top Box Source Start
/// </summary>
[JoinName("Display1SetTopBoxSourceStart")] [JoinName("Display1SetTopBoxSourceStart")]
public JoinDataComplete Display1SetTopBoxSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 188, JoinSpan = 5, AttributeName = "Display 1 - Source TV" }, public JoinDataComplete Display1SetTopBoxSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 185, JoinSpan = 5, AttributeName = "Display 1 - Source TV" },
new JoinMetadata { Description = "Display 1 - Source TV", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital }); new JoinMetadata { Description = "Display 1 - Source TV", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital });
// Display 1 // Display 1
/// <summary>
/// Display 1 Start
/// </summary>
[JoinName("Display1Start")] [JoinName("Display1Start")]
public JoinDataComplete Display1Start = new JoinDataComplete(new JoinData { JoinNumber = 158, JoinSpan = 1 }, public JoinDataComplete Display1Start = new JoinDataComplete(new JoinData { JoinNumber = 190, JoinSpan = 1 },
new JoinMetadata { Description = "Display 1 Start", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital }); new JoinMetadata { Description = "Display 1 Start", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital });
/// <summary> /// <summary>
/// Constructor to use when instantiating this Join Map without inheriting from it /// Constructor to use when instantiating this Join Map without inheriting from it
/// </summary> /// </summary>

View File

@@ -1,6 +1,4 @@
 using Crestron.SimplSharp;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO; using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharp.CrestronXml; using Crestron.SimplSharp.CrestronXml;
using Crestron.SimplSharp.CrestronXml.Serialization; using Crestron.SimplSharp.CrestronXml.Serialization;
@@ -8,6 +6,7 @@ using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.Fusion; using Crestron.SimplSharpPro.Fusion;
using Newtonsoft.Json; using Newtonsoft.Json;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.DeviceTypeInterfaces; using PepperDash.Essentials.Core.DeviceTypeInterfaces;
using Serilog.Events; using Serilog.Events;
@@ -21,26 +20,42 @@ namespace PepperDash.Essentials.Core.Fusion
/// <summary> /// <summary>
/// Represents a EssentialsHuddleSpaceFusionSystemControllerBase /// Represents a EssentialsHuddleSpaceFusionSystemControllerBase
/// </summary> /// </summary>
public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupancyStatusProvider public class IEssentialsRoomFusionController : EssentialsDevice, IOccupancyStatusProvider, IFusionHelpRequest
{ {
private readonly EssentialsHuddleSpaceRoomFusionRoomJoinMap JoinMap; private IEssentialsRoomFusionControllerPropertiesConfig _config;
private EssentialsHuddleSpaceRoomFusionRoomJoinMap JoinMap;
private const string RemoteOccupancyXml = "<Occupancy><Type>Local</Type><State>{0}</State></Occupancy>"; private const string RemoteOccupancyXml = "<Occupancy><Type>Local</Type><State>{0}</State></Occupancy>";
private readonly bool _guidFileExists; private bool _guidFileExists;
private readonly Dictionary<Device, BoolInputSig> _sourceToFeedbackSigs = private readonly Dictionary<Device, BoolInputSig> _sourceToFeedbackSigs =
new Dictionary<Device, BoolInputSig>(); new Dictionary<Device, BoolInputSig>();
/// <summary>
/// Gets or sets the CurrentRoomSourceNameSig
/// </summary>
protected StringSigData CurrentRoomSourceNameSig; protected StringSigData CurrentRoomSourceNameSig;
private readonly FusionCustomPropertiesBridge CustomPropertiesBridge = new FusionCustomPropertiesBridge(); private readonly FusionCustomPropertiesBridge CustomPropertiesBridge = new FusionCustomPropertiesBridge();
/// <summary>
/// Gets or sets the FusionOccSensor
/// </summary>
protected FusionOccupancySensorAsset FusionOccSensor; protected FusionOccupancySensorAsset FusionOccSensor;
private readonly FusionRemoteOccupancySensor FusionRemoteOccSensor; private readonly FusionRemoteOccupancySensor FusionRemoteOccSensor;
/// <summary>
/// Gets or sets the FusionRoom
/// </summary>
protected FusionRoom FusionRoom; protected FusionRoom FusionRoom;
/// <summary>
/// Gets or sets the FusionStaticAssets
/// </summary>
protected Dictionary<int, FusionAsset> FusionStaticAssets; protected Dictionary<int, FusionAsset> FusionStaticAssets;
private readonly long PushNotificationTimeout = 5000; private readonly long PushNotificationTimeout = 5000;
private readonly IEssentialsRoom Room; private IEssentialsRoom Room;
private readonly long SchedulePollInterval = 300000; private readonly long SchedulePollInterval = 300000;
private Event _currentMeeting; private Event _currentMeeting;
@@ -48,8 +63,7 @@ namespace PepperDash.Essentials.Core.Fusion
private CTimer _dailyTimeRequestTimer; private CTimer _dailyTimeRequestTimer;
private StatusMonitorCollection _errorMessageRollUp; private StatusMonitorCollection _errorMessageRollUp;
private FusionRoomGuids _guiDs; private FusionRoomGuids _guids;
private uint _ipId;
private bool _isRegisteredForSchedulePushNotifications; private bool _isRegisteredForSchedulePushNotifications;
private Event _nextMeeting; private Event _nextMeeting;
@@ -60,6 +74,20 @@ namespace PepperDash.Essentials.Core.Fusion
private string _roomOccupancyRemoteString; private string _roomOccupancyRemoteString;
private bool _helpRequestSent;
private eFusionHelpResponse _helpRequestStatus;
/// <inheritdoc />
public StringFeedback HelpRequestResponseFeedback { get; private set; }
/// <inheritdoc />
public BoolFeedback HelpRequestSentFeedback { get; private set; }
/// <inheritdoc />
public StringFeedback HelpRequestStatusFeedback { get; private set; }
#region System Info Sigs #region System Info Sigs
//StringSigData SystemName; //StringSigData SystemName;
@@ -93,71 +121,110 @@ namespace PepperDash.Essentials.Core.Fusion
#endregion #endregion
public EssentialsHuddleSpaceFusionSystemControllerBase(IEssentialsRoom room, uint ipId, string joinMapKey) /// <summary>
/// Constructor
/// </summary>
public IEssentialsRoomFusionController(string key, string name, IEssentialsRoomFusionControllerPropertiesConfig config)
: base(key, name)
{
_config = config;
AddPostActivationAction(() =>
{
var room = DeviceManager.GetDeviceForKey<IEssentialsRoom>(_config.RoomKey);
if (room == null)
{
this.LogError("Error Creating Fusion Room Controller. No room found with key '{0}'", _config.RoomKey);
return;
}
this.LogInformation("Creating Fusion Room Controller for room '{0}' at IPID: {1:X2}", room.Key, _config.IpIdInt);
ConstructorHelper(room, _config.IpIdInt, _config.JoinMapKey);
});
}
/// <summary>
///
/// </summary>
/// <param name="room"></param>
/// <param name="ipId"></param>
/// <param name="joinMapKey"></param>
public IEssentialsRoomFusionController(IEssentialsRoom room, string ipId, string joinMapKey)
: base(room.Key + "-fusion") : base(room.Key + "-fusion")
{
_config = new IEssentialsRoomFusionControllerPropertiesConfig()
{
IpId = ipId,
RoomKey = room.Key,
JoinMapKey = joinMapKey
};
ConstructorHelper(room, _config.IpIdInt, joinMapKey);
}
private void ConstructorHelper(IEssentialsRoom room, uint ipId, string joinMapKey)
{ {
try try
{ {
this.LogDebug("ConstructorHelper called for Fusion Room Controller for room '{0}' with IPID {1:X2}", room.Key, ipId);
this.LogDebug("JoinMap Key: {0}", joinMapKey);
JoinMap = new EssentialsHuddleSpaceRoomFusionRoomJoinMap(1); JoinMap = new EssentialsHuddleSpaceRoomFusionRoomJoinMap(1);
CrestronConsole.AddNewConsoleCommand((o) => JoinMap.PrintJoinMapInfo(), string.Format("ptjnmp-{0}", Key), "Prints Attribute Join Map", ConsoleAccessLevelEnum.AccessOperator); this.LogDebug("JoinMap created");
CrestronConsole.AddNewConsoleCommand((o) =>
{
if (o is string deviceKey)
{
if (string.IsNullOrEmpty(deviceKey) || deviceKey == "?")
{
CrestronConsole.ConsoleCommandResponse("Please provide a device key for a Fusion Room instance");
return;
}
else if (deviceKey != this.Key)
{
return;
}
}
else
{
CrestronConsole.ConsoleCommandResponse("Invalid parameter. Please provide a device key for a Fusion Room instance");
return;
}
JoinMap.PrintJoinMapInfo();
}, "printfusionjoinmap", "Prints Attribute Join Map", ConsoleAccessLevelEnum.AccessOperator);
if (!string.IsNullOrEmpty(joinMapKey)) if (!string.IsNullOrEmpty(joinMapKey))
{ {
// this.LogDebug("Attempting to get custom join map for key: {0}", joinMapKey);
var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey); var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey);
if (customJoins != null) if (customJoins != null)
{ {
JoinMap.SetCustomJoinData(customJoins); JoinMap.SetCustomJoinData(customJoins);
} }
} }
Room = room; Room = room;
_ipId = ipId; this.LogDebug("Room found: {0}", Room.Key);
FusionStaticAssets = new Dictionary<int, FusionAsset>(); FusionStaticAssets = new Dictionary<int, FusionAsset>();
_guiDs = new FusionRoomGuids(); this.LogDebug("FusionStaticAssets dictionary created");
var mac = _guids = new FusionRoomGuids();
CrestronEthernetHelper.GetEthernetParameter(
CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0);
var slot = Global.ControlSystem.ProgramNumber;
var guidFilePath = Global.FilePathPrefix +
string.Format(@"{0}-FusionGuids-{1:X2}.json", InitialParametersClass.ProgramIDTag, _ipId);
var oldGuidFilePath = Global.FilePathPrefix +
string.Format(@"{0}-FusionGuids.json", InitialParametersClass.ProgramIDTag);
if (File.Exists(oldGuidFilePath))
{
Debug.LogMessage(LogEventLevel.Information, this, "Migrating from old Fusion GUID file to new Fusion GUID File");
File.Copy(oldGuidFilePath, guidFilePath);
File.Delete(oldGuidFilePath);
}
_guidFileExists = File.Exists(guidFilePath);
// Check if file exists
if (!_guidFileExists)
{
// Does not exist. Create GUIDs
_guiDs = new FusionRoomGuids(Room.Name, ipId, _guiDs.GenerateNewRoomGuid(slot, mac),
FusionStaticAssets);
}
else
{
// Exists. Read GUIDs
ReadGuidFile(guidFilePath);
}
this.LogDebug("FusionRoomGuids created");
if (Room is IRoomOccupancy occupancyRoom) if (Room is IRoomOccupancy occupancyRoom)
{ {
Debug.LogDebug(this, "Room '{0}' supports IRoomOccupancy", Room.Key);
if (occupancyRoom.RoomOccupancy != null) if (occupancyRoom.RoomOccupancy != null)
{ {
if (occupancyRoom.OccupancyStatusProviderIsRemote) if (occupancyRoom.OccupancyStatusProviderIsRemote)
@@ -171,8 +238,8 @@ namespace PepperDash.Essentials.Core.Fusion
} }
} }
this.LogDebug("Occupancy setup complete");
AddPostActivationAction(() => PostActivate(guidFilePath));
} }
catch (Exception e) catch (Exception e)
{ {
@@ -180,9 +247,54 @@ namespace PepperDash.Essentials.Core.Fusion
} }
} }
private void PostActivate(string guidFilePath) private string GetGuidFilePath(uint ipId)
{ {
CreateSymbolAndBasicSigs(_ipId); var mac =
CrestronEthernetHelper.GetEthernetParameter(
CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0);
var slot = Global.ControlSystem.ProgramNumber;
var guidFilePath = Global.FilePathPrefix +
string.Format(@"{0}-FusionGuids-{1:X2}.json", InitialParametersClass.ProgramIDTag, _config.IpIdInt);
var oldGuidFilePath = Global.FilePathPrefix +
string.Format(@"{0}-FusionGuids.json", InitialParametersClass.ProgramIDTag);
if (File.Exists(oldGuidFilePath))
{
Debug.LogMessage(LogEventLevel.Information, this, "Migrating from old Fusion GUID file to new Fusion GUID File");
File.Copy(oldGuidFilePath, guidFilePath);
File.Delete(oldGuidFilePath);
}
_guidFileExists = File.Exists(guidFilePath);
// Check if file exists
if (!_guidFileExists)
{
// Does not exist. Create GUIDs
_guids = new FusionRoomGuids(Room.Name, ipId, _guids.GenerateNewRoomGuid(slot, mac),
FusionStaticAssets);
}
else
{
// Exists. Read GUIDs
ReadGuidFile(guidFilePath);
}
return guidFilePath;
}
/// <inheritdoc />
public override void Initialize()
{
GenerateGuidFile(GetGuidFilePath(_config.IpIdInt));
CreateSymbolAndBasicSigs(_config.IpIdInt);
SetUpSources(); SetUpSources();
SetUpCommunitcationMonitors(); SetUpCommunitcationMonitors();
SetUpDisplay(); SetUpDisplay();
@@ -191,12 +303,18 @@ namespace PepperDash.Essentials.Core.Fusion
FusionRVI.GenerateFileForAllFusionDevices(); FusionRVI.GenerateFileForAllFusionDevices();
GenerateGuidFile(guidFilePath); HelpRequestResponseFeedback = new StringFeedback("HelpRequestResponse", () => FusionRoom.Help.OutputSig.StringValue);
HelpRequestSentFeedback = new BoolFeedback("HelpRequestSent", () => _helpRequestSent);
HelpRequestStatusFeedback = new StringFeedback("HelpRequestStatus", () => _helpRequestStatus.ToString());
} }
/// <summary>
/// Gets the RoomGuid
/// </summary>
protected string RoomGuid protected string RoomGuid
{ {
get { return _guiDs.RoomGuid; } get { return _guids.RoomGuid; }
} }
/// <summary> /// <summary>
@@ -204,6 +322,9 @@ namespace PepperDash.Essentials.Core.Fusion
/// </summary> /// </summary>
public StringFeedback RoomOccupancyRemoteStringFeedback { get; private set; } public StringFeedback RoomOccupancyRemoteStringFeedback { get; private set; }
/// <summary>
/// Gets the RoomIsOccupiedFeedbackFunc
/// </summary>
protected Func<bool> RoomIsOccupiedFeedbackFunc protected Func<bool> RoomIsOccupiedFeedbackFunc
{ {
get { return () => FusionRemoteOccSensor.RoomOccupied.OutputSig.BoolValue; } get { return () => FusionRemoteOccSensor.RoomOccupied.OutputSig.BoolValue; }
@@ -218,10 +339,16 @@ namespace PepperDash.Essentials.Core.Fusion
#endregion #endregion
/// <summary>
/// ScheduleChange event
/// </summary>
public event EventHandler<ScheduleChangeEventArgs> ScheduleChange; public event EventHandler<ScheduleChangeEventArgs> ScheduleChange;
//public event EventHandler<MeetingChangeEventArgs> MeetingEndWarning; //public event EventHandler<MeetingChangeEventArgs> MeetingEndWarning;
//public event EventHandler<MeetingChangeEventArgs> NextMeetingBeginWarning; //public event EventHandler<MeetingChangeEventArgs> NextMeetingBeginWarning;
/// <summary>
/// RoomInfoChange event
/// </summary>
public event EventHandler<EventArgs> RoomInfoChange; public event EventHandler<EventArgs> RoomInfoChange;
//ScheduleResponseEvent NextMeeting; //ScheduleResponseEvent NextMeeting;
@@ -258,11 +385,11 @@ namespace PepperDash.Essentials.Core.Fusion
Debug.LogMessage(LogEventLevel.Debug, this, "Writing GUIDs to file"); Debug.LogMessage(LogEventLevel.Debug, this, "Writing GUIDs to file");
_guiDs = FusionOccSensor == null _guids = FusionOccSensor == null
? new FusionRoomGuids(Room.Name, _ipId, RoomGuid, FusionStaticAssets) ? new FusionRoomGuids(Room.Name, _config.IpIdInt, RoomGuid, FusionStaticAssets)
: new FusionRoomGuids(Room.Name, _ipId, RoomGuid, FusionStaticAssets, FusionOccSensor); : new FusionRoomGuids(Room.Name, _config.IpIdInt, RoomGuid, FusionStaticAssets, FusionOccSensor);
var json = JsonConvert.SerializeObject(_guiDs, Newtonsoft.Json.Formatting.Indented); var json = JsonConvert.SerializeObject(_guids, Newtonsoft.Json.Formatting.Indented);
using (var sw = new StreamWriter(filePath)) using (var sw = new StreamWriter(filePath))
{ {
@@ -312,17 +439,17 @@ namespace PepperDash.Essentials.Core.Fusion
{ {
var json = File.ReadToEnd(filePath, Encoding.ASCII); var json = File.ReadToEnd(filePath, Encoding.ASCII);
_guiDs = JsonConvert.DeserializeObject<FusionRoomGuids>(json); _guids = JsonConvert.DeserializeObject<FusionRoomGuids>(json);
_ipId = _guiDs.IpId; // _config.IpId = _guids.IpId;
FusionStaticAssets = _guiDs.StaticAssets; FusionStaticAssets = _guids.StaticAssets;
} }
Debug.LogMessage(LogEventLevel.Information, this, "Fusion Guids successfully read from file: {0}", Debug.LogMessage(LogEventLevel.Information, this, "Fusion Guids successfully read from file: {0}",
filePath); filePath);
Debug.LogMessage(LogEventLevel.Debug, this, "\r\n********************\r\n\tRoom Name: {0}\r\n\tIPID: {1:X}\r\n\tRoomGuid: {2}\r\n*******************", Room.Name, _ipId, RoomGuid); Debug.LogMessage(LogEventLevel.Debug, this, "\r\n********************\r\n\tRoom Name: {0}\r\n\tIPID: {1:X}\r\n\tRoomGuid: {2}\r\n*******************", Room.Name, _config.IpIdInt, RoomGuid);
foreach (var item in FusionStaticAssets) foreach (var item in FusionStaticAssets)
{ {
@@ -343,6 +470,10 @@ namespace PepperDash.Essentials.Core.Fusion
} }
} }
/// <summary>
/// CreateSymbolAndBasicSigs method
/// </summary>
/// <param name="ipId"></param>
protected virtual void CreateSymbolAndBasicSigs(uint ipId) protected virtual void CreateSymbolAndBasicSigs(uint ipId)
{ {
Debug.LogMessage(LogEventLevel.Information, this, "Creating Fusion Room symbol with GUID: {0} and IP-ID {1:X2}", RoomGuid, ipId); Debug.LogMessage(LogEventLevel.Information, this, "Creating Fusion Room symbol with GUID: {0} and IP-ID {1:X2}", RoomGuid, ipId);
@@ -405,6 +536,10 @@ namespace PepperDash.Essentials.Core.Fusion
CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler; CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler;
} }
/// <summary>
/// CrestronEnvironment_EthernetEventHandler method
/// </summary>
/// <param name="ethernetEventArgs"></param>
protected void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs) protected void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs)
{ {
if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp) if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp)
@@ -413,6 +548,9 @@ namespace PepperDash.Essentials.Core.Fusion
} }
} }
/// <summary>
/// GetSystemInfo method
/// </summary>
protected void GetSystemInfo() protected void GetSystemInfo()
{ {
//SystemName.InputSig.StringValue = Room.Name; //SystemName.InputSig.StringValue = Room.Name;
@@ -426,6 +564,9 @@ namespace PepperDash.Essentials.Core.Fusion
() => CrestronConsole.SendControlSystemCommand("reboot", ref response)); () => CrestronConsole.SendControlSystemCommand("reboot", ref response));
} }
/// <summary>
/// SetUpEthernetValues method
/// </summary>
protected void SetUpEthernetValues() protected void SetUpEthernetValues()
{ {
_ip1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorIp1.JoinNumber, JoinMap.ProcessorIp1.AttributeName, eSigIoMask.InputSigOnly); _ip1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorIp1.JoinNumber, JoinMap.ProcessorIp1.AttributeName, eSigIoMask.InputSigOnly);
@@ -441,6 +582,9 @@ namespace PepperDash.Essentials.Core.Fusion
_netMask2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorNetMask2.JoinNumber, JoinMap.ProcessorNetMask2.AttributeName, eSigIoMask.InputSigOnly); _netMask2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorNetMask2.JoinNumber, JoinMap.ProcessorNetMask2.AttributeName, eSigIoMask.InputSigOnly);
} }
/// <summary>
/// GetProcessorEthernetValues method
/// </summary>
protected void GetProcessorEthernetValues() protected void GetProcessorEthernetValues()
{ {
_ip1.InputSig.StringValue = _ip1.InputSig.StringValue =
@@ -475,7 +619,7 @@ namespace PepperDash.Essentials.Core.Fusion
// Interface 1 // Interface 1
if (InitialParametersClass.NumberOfEthernetInterfaces > 1) if (InitialParametersClass.NumberOfEthernetInterfaces > 1)
// Only get these values if the processor has more than 1 NIC // Only get these values if the processor has more than 1 NIC
{ {
_ip2.InputSig.StringValue = _ip2.InputSig.StringValue =
CrestronEthernetHelper.GetEthernetParameter( CrestronEthernetHelper.GetEthernetParameter(
@@ -489,6 +633,9 @@ namespace PepperDash.Essentials.Core.Fusion
} }
} }
/// <summary>
/// GetProcessorInfo method
/// </summary>
protected void GetProcessorInfo() protected void GetProcessorInfo()
{ {
_firmware = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorFirmware.JoinNumber, JoinMap.ProcessorFirmware.AttributeName, eSigIoMask.InputSigOnly); _firmware = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorFirmware.JoinNumber, JoinMap.ProcessorFirmware.AttributeName, eSigIoMask.InputSigOnly);
@@ -499,7 +646,7 @@ namespace PepperDash.Essentials.Core.Fusion
{ {
var join = JoinMap.ProgramNameStart.JoinNumber + i; var join = JoinMap.ProgramNameStart.JoinNumber + i;
var progNum = i + 1; var progNum = i + 1;
_program[i] = FusionRoom.CreateOffsetStringSig((uint) join, _program[i] = FusionRoom.CreateOffsetStringSig((uint)join,
string.Format("{0} {1}", JoinMap.ProgramNameStart.AttributeName, progNum), eSigIoMask.InputSigOnly); string.Format("{0} {1}", JoinMap.ProgramNameStart.AttributeName, progNum), eSigIoMask.InputSigOnly);
} }
} }
@@ -507,6 +654,9 @@ namespace PepperDash.Essentials.Core.Fusion
_firmware.InputSig.StringValue = InitialParametersClass.FirmwareVersion; _firmware.InputSig.StringValue = InitialParametersClass.FirmwareVersion;
} }
/// <summary>
/// GetCustomProperties method
/// </summary>
protected void GetCustomProperties() protected void GetCustomProperties()
{ {
if (FusionRoom.IsOnline) if (FusionRoom.IsOnline)
@@ -524,11 +674,16 @@ namespace PepperDash.Essentials.Core.Fusion
// TODO: Get IP and Project Name from TP // TODO: Get IP and Project Name from TP
} }
/// <summary>
/// FusionRoom_OnlineStatusChange method
/// </summary>
/// <param name="currentDevice"></param>
/// <param name="args"></param>
protected void FusionRoom_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args) protected void FusionRoom_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args)
{ {
if (args.DeviceOnLine) if (args.DeviceOnLine)
{ {
CrestronInvoke.BeginInvoke( (o) => CrestronInvoke.BeginInvoke((o) =>
{ {
CrestronEnvironment.Sleep(200); CrestronEnvironment.Sleep(200);
@@ -676,7 +831,7 @@ namespace PepperDash.Essentials.Core.Fusion
var extendTime = _currentMeeting.dtEnd - DateTime.Now; var extendTime = _currentMeeting.dtEnd - DateTime.Now;
var extendMinutesRaw = extendTime.TotalMinutes; var extendMinutesRaw = extendTime.TotalMinutes;
extendMinutes += (int) Math.Round(extendMinutesRaw); extendMinutes += (int)Math.Round(extendMinutesRaw);
} }
@@ -784,11 +939,11 @@ namespace PepperDash.Essentials.Core.Fusion
var parameters = actionResponse["Parameters"]; var parameters = actionResponse["Parameters"];
foreach (var isRegistered in from XmlElement parameter in parameters foreach (var isRegistered in from XmlElement parameter in parameters
where parameter.HasAttributes where parameter.HasAttributes
select parameter.Attributes select parameter.Attributes
into attributes into attributes
where attributes["ID"].Value == "Registered" where attributes["ID"].Value == "Registered"
select Int32.Parse(attributes["Value"].Value)) select Int32.Parse(attributes["Value"].Value))
{ {
switch (isRegistered) switch (isRegistered)
{ {
@@ -845,9 +1000,9 @@ namespace PepperDash.Essentials.Core.Fusion
Debug.LogMessage(LogEventLevel.Debug, this, "DateTime from Fusion Server: {0}", currentTime); Debug.LogMessage(LogEventLevel.Debug, this, "DateTime from Fusion Server: {0}", currentTime);
// Parse time and date from response and insert values // Parse time and date from response and insert values
CrestronEnvironment.SetTimeAndDate((ushort) currentTime.Hour, (ushort) currentTime.Minute, CrestronEnvironment.SetTimeAndDate((ushort)currentTime.Hour, (ushort)currentTime.Minute,
(ushort) currentTime.Second, (ushort) currentTime.Month, (ushort) currentTime.Day, (ushort)currentTime.Second, (ushort)currentTime.Month, (ushort)currentTime.Day,
(ushort) currentTime.Year); (ushort)currentTime.Year);
Debug.LogMessage(LogEventLevel.Debug, this, "Processor time set to {0}", CrestronEnvironment.GetLocalTime()); Debug.LogMessage(LogEventLevel.Debug, this, "Processor time set to {0}", CrestronEnvironment.GetLocalTime());
} }
@@ -1065,6 +1220,9 @@ namespace PepperDash.Essentials.Core.Fusion
} }
} }
/// <summary>
/// SetUpSources method
/// </summary>
protected virtual void SetUpSources() protected virtual void SetUpSources()
{ {
// Sources // Sources
@@ -1074,10 +1232,10 @@ namespace PepperDash.Essentials.Core.Fusion
// NEW PROCESS: // NEW PROCESS:
// Make these lists and insert the fusion attributes by iterating these // Make these lists and insert the fusion attributes by iterating these
var setTopBoxes = dict.Where(d => d.Value.SourceDevice is ISetTopBoxControls); var setTopBoxes = dict.Where(d => d.Value.SourceDevice is ISetTopBoxControls);
uint i = 1; uint i = 0;
foreach (var kvp in setTopBoxes) foreach (var kvp in setTopBoxes)
{ {
TryAddRouteActionSigs(JoinMap.Display1SetTopBoxSourceStart.AttributeName + " " + i, JoinMap.Display1SetTopBoxSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); TryAddRouteActionSigs(JoinMap.Display1SetTopBoxSourceStart.AttributeName + " " + (i + 1), JoinMap.Display1SetTopBoxSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice);
i++; i++;
if (i > JoinMap.Display1SetTopBoxSourceStart.JoinSpan) // We only have five spots if (i > JoinMap.Display1SetTopBoxSourceStart.JoinSpan) // We only have five spots
{ {
@@ -1086,10 +1244,10 @@ namespace PepperDash.Essentials.Core.Fusion
} }
var discPlayers = dict.Where(d => d.Value.SourceDevice is IDiscPlayerControls); var discPlayers = dict.Where(d => d.Value.SourceDevice is IDiscPlayerControls);
i = 1; i = 0;
foreach (var kvp in discPlayers) foreach (var kvp in discPlayers)
{ {
TryAddRouteActionSigs(JoinMap.Display1DiscPlayerSourceStart.AttributeName + " " + i, JoinMap.Display1DiscPlayerSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); TryAddRouteActionSigs(JoinMap.Display1DiscPlayerSourceStart.AttributeName + " " + (i + 1), JoinMap.Display1DiscPlayerSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice);
i++; i++;
if (i > JoinMap.Display1DiscPlayerSourceStart.JoinSpan) // We only have five spots if (i > JoinMap.Display1DiscPlayerSourceStart.JoinSpan) // We only have five spots
{ {
@@ -1098,10 +1256,10 @@ namespace PepperDash.Essentials.Core.Fusion
} }
var laptops = dict.Where(d => d.Value.SourceDevice is IRoutingSource); var laptops = dict.Where(d => d.Value.SourceDevice is IRoutingSource);
i = 1; i = 0;
foreach (var kvp in laptops) foreach (var kvp in laptops)
{ {
TryAddRouteActionSigs(JoinMap.Display1LaptopSourceStart.AttributeName + " " + i, JoinMap.Display1LaptopSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); TryAddRouteActionSigs(JoinMap.Display1LaptopSourceStart.AttributeName + " " + (i + 1), JoinMap.Display1LaptopSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice);
i++; i++;
if (i > JoinMap.Display1LaptopSourceStart.JoinSpan) // We only have ten spots??? if (i > JoinMap.Display1LaptopSourceStart.JoinSpan) // We only have ten spots???
{ {
@@ -1111,7 +1269,7 @@ namespace PepperDash.Essentials.Core.Fusion
foreach (var usageDevice in dict.Select(kvp => kvp.Value.SourceDevice).OfType<IUsageTracking>()) foreach (var usageDevice in dict.Select(kvp => kvp.Value.SourceDevice).OfType<IUsageTracking>())
{ {
usageDevice.UsageTracker = new UsageTracking(usageDevice as Device) {UsageIsTracked = true}; usageDevice.UsageTracker = new UsageTracking(usageDevice as Device) { UsageIsTracked = true };
usageDevice.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded; usageDevice.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded;
} }
} }
@@ -1157,17 +1315,31 @@ namespace PepperDash.Essentials.Core.Fusion
Debug.LogMessage(LogEventLevel.Debug, this, "Device usage string: {0}", deviceUsage); Debug.LogMessage(LogEventLevel.Debug, this, "Device usage string: {0}", deviceUsage);
} }
/// <summary>
/// Tries to add route action sigs for a source
/// </summary>
/// <param name="attrName"></param>
/// <param name="attrNum"></param>
/// <param name="routeKey"></param>
/// <param name="pSrc"></param>
protected void TryAddRouteActionSigs(string attrName, uint attrNum, string routeKey, Device pSrc) protected void TryAddRouteActionSigs(string attrName, uint attrNum, string routeKey, Device pSrc)
{ {
Debug.LogMessage(LogEventLevel.Verbose, this, "Creating attribute '{0}' with join {1} for source {2}", this.LogVerbose("Creating attribute '{0}' with join {1} for source {2}",
attrName, attrNum, pSrc.Key); attrName, attrNum, pSrc.Key);
try try
{ {
var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputOutputSig); var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputOutputSig);
// Need feedback when this source is selected // Need feedback when this source is selected
// Event handler, added below, will compare source changes with this sig dict // Event handler, added below, will compare source changes with this sig dict
_sourceToFeedbackSigs.Add(pSrc, sigD.InputSig); if (!_sourceToFeedbackSigs.ContainsKey(pSrc))
{
_sourceToFeedbackSigs.Add(pSrc, sigD.InputSig);
}
else
{
this.LogWarning("Source '{0}' already has a feedback sig mapped. Overwriting.", pSrc.Key);
_sourceToFeedbackSigs[pSrc] = sigD.InputSig;
}
// And respond to selection in Fusion // And respond to selection in Fusion
sigD.OutputSig.SetSigFalseAction(() => sigD.OutputSig.SetSigFalseAction(() =>
@@ -1180,14 +1352,12 @@ namespace PepperDash.Essentials.Core.Fusion
} }
catch (Exception) catch (Exception)
{ {
Debug.LogMessage(LogEventLevel.Verbose, this, "Error creating Fusion signal {0} {1} for device '{2}'. THIS NEEDS REWORKING", this.LogVerbose("Error creating Fusion signal {0} {1} for device '{2}'. THIS NEEDS REWORKING",
attrNum, attrName, pSrc.Key); attrNum, attrName, pSrc.Key);
} }
} }
/// <summary>
///
/// </summary>
private void SetUpCommunitcationMonitors() private void SetUpCommunitcationMonitors()
{ {
uint displayNum = 0; uint displayNum = 0;
@@ -1274,6 +1444,8 @@ namespace PepperDash.Essentials.Core.Fusion
if (attrName != null) if (attrName != null)
{ {
this.LogDebug("Linking communication monitor for device '{0}' to Fusion attribute '{1}' at join {2}",
dev.Key, attrName, attrNum);
// Link comm status to sig and update // Link comm status to sig and update
var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputSigOnly); var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputSigOnly);
var smd = dev as ICommunicationMonitor; var smd = dev as ICommunicationMonitor;
@@ -1285,6 +1457,9 @@ namespace PepperDash.Essentials.Core.Fusion
} }
} }
/// <summary>
/// SetUpDisplay method
/// </summary>
protected virtual void SetUpDisplay() protected virtual void SetUpDisplay()
{ {
try try
@@ -1297,7 +1472,7 @@ namespace PepperDash.Essentials.Core.Fusion
foreach (var display in displays.Cast<IDisplay>()) foreach (var display in displays.Cast<IDisplay>())
{ {
display.UsageTracker = new UsageTracking(display as Device) {UsageIsTracked = true}; display.UsageTracker = new UsageTracking(display as Device) { UsageIsTracked = true };
display.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded; display.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded;
} }
@@ -1410,7 +1585,7 @@ namespace PepperDash.Essentials.Core.Fusion
// Power on // Power on
var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint) joinOffset, displayName + "Power On", var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint)joinOffset, displayName + "Power On",
eSigIoMask.InputOutputSig); eSigIoMask.InputOutputSig);
defaultDisplayPowerOn.OutputSig.UserObject = new Action<bool>(b => defaultDisplayPowerOn.OutputSig.UserObject = new Action<bool>(b =>
{ {
@@ -1421,7 +1596,7 @@ namespace PepperDash.Essentials.Core.Fusion
}); });
// Power Off // Power Off
var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint) joinOffset + 1, displayName + "Power Off", var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 1, displayName + "Power Off",
eSigIoMask.InputOutputSig); eSigIoMask.InputOutputSig);
defaultDisplayPowerOn.OutputSig.UserObject = new Action<bool>(b => defaultDisplayPowerOn.OutputSig.UserObject = new Action<bool>(b =>
{ {
@@ -1439,7 +1614,7 @@ namespace PepperDash.Essentials.Core.Fusion
} }
// Current Source // Current Source
var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint) joinOffset + 8, var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 8,
displayName + "Source None", eSigIoMask.InputOutputSig); displayName + "Source None", eSigIoMask.InputOutputSig);
defaultDisplaySourceNone.OutputSig.UserObject = new Action<bool>(b => defaultDisplaySourceNone.OutputSig.UserObject = new Action<bool>(b =>
{ {
@@ -1507,7 +1682,7 @@ namespace PepperDash.Essentials.Core.Fusion
//if (Room.OccupancyObj != null) //if (Room.OccupancyObj != null)
//{ //{
var tempOccAsset = _guiDs.OccupancyAsset; var tempOccAsset = _guids.OccupancyAsset;
if (tempOccAsset == null) if (tempOccAsset == null)
{ {
@@ -1532,7 +1707,7 @@ namespace PepperDash.Essentials.Core.Fusion
occRoom.RoomOccupancy.RoomIsOccupiedFeedback.OutputChange += RoomIsOccupiedFeedback_OutputChange; occRoom.RoomOccupancy.RoomIsOccupiedFeedback.OutputChange += RoomIsOccupiedFeedback_OutputChange;
} }
RoomOccupancyRemoteStringFeedback = new StringFeedback(() => _roomOccupancyRemoteString); RoomOccupancyRemoteStringFeedback = new StringFeedback(() => _roomOccupancyRemoteString);
RoomOccupancyRemoteStringFeedback.LinkInputSig(occSensorAsset.RoomOccupancyInfo.InputSig); RoomOccupancyRemoteStringFeedback.LinkInputSig(occSensorAsset.RoomOccupancyInfo.InputSig);
//} //}
@@ -1588,12 +1763,74 @@ namespace PepperDash.Essentials.Core.Fusion
} }
} }
/// <summary>
/// Event handler for Fusion state changes
/// </summary>
/// <param name="device"></param>
/// <param name="args"></param>
protected void FusionRoom_FusionStateChange(FusionBase device, FusionStateEventArgs args) protected void FusionRoom_FusionStateChange(FusionBase device, FusionStateEventArgs args)
{ {
if (args.EventId == FusionEventIds.HelpMessageReceivedEventId)
{
this.LogInformation( "Help message received from Fusion for room '{0}'",
Room.Name);
this.LogDebug("Help message content: {0}", FusionRoom.Help.OutputSig.StringValue);
// Fire help request event
HelpRequestResponseFeedback.FireUpdate();
if (!string.IsNullOrEmpty(FusionRoom.Help.OutputSig.StringValue))
{
switch (FusionRoom.Help.OutputSig.StringValue)
{
case "Please wait, a technician is on his / her way.":
// this.LogInformation("Please wait, a technician is on his / her way.",
// Room.Name);
_helpRequestStatus = eFusionHelpResponse.HelpOnTheWay;
break;
case "Please call the helpdesk.":
// this.LogInformation("Please call the helpdesk.");
// _helpRequestStatus = eFusionHelpResponse.CallHelpDesk;
break;
case "Please wait, I will reschedule your meeting to a different room.":
// this.LogInformation("Please wait, I will reschedule your meeting to a different room.",
// Room.Name);
_helpRequestStatus = eFusionHelpResponse.ReschedulingMeeting;
break;
case "I will be taking control of your system. Please be patient while I adjust the settings.":
// this.LogInformation("I will be taking control of your system. Please be patient while I adjust the settings.",
// Room.Name);
_helpRequestStatus = eFusionHelpResponse.TakingControl;
break;
default:
// this.LogInformation("Unknown help request code received from Fusion for room '{0}'",
// Room.Name);
_helpRequestStatus = eFusionHelpResponse.None;
break;
}
}
else
{
_helpRequestStatus = eFusionHelpResponse.None;
}
if(_helpRequestStatus == eFusionHelpResponse.None)
{
_helpRequestSent = false;
HelpRequestSentFeedback.FireUpdate();
}
HelpRequestStatusFeedback.FireUpdate();
}
// The sig/UO method: Need separate handlers for fixed and user sigs, all flavors, // The sig/UO method: Need separate handlers for fixed and user sigs, all flavors,
// even though they all contain sigs. // even though they all contain sigs.
BoolOutputSig outSig; BoolOutputSig outSig;
if (args.UserConfiguredSigDetail is BooleanSigDataFixedName sigData) if (args.UserConfiguredSigDetail is BooleanSigDataFixedName sigData)
{ {
@@ -1632,9 +1869,69 @@ namespace PepperDash.Essentials.Core.Fusion
(outSig.UserObject as Action<string>).Invoke(outSig.StringValue); (outSig.UserObject as Action<string>).Invoke(outSig.StringValue);
} }
} }
/// <inheritdoc />
public void SendHelpRequest()
{
var now = DateTime.Now;
var breakString = _config.UseHtmlFormatForHelpRequests ? "<BR>" : "\r\n";
var date = now.ToString("MMMM dd, yyyy");
var time = now.ToString("hh:mm tt");
if (_config.Use24HourTimeFormat)
{
time = now.ToString("HH:mm");
}
var requestString = $"HR00: {breakString} Assistance has been requested from room {Room.Name}{breakString}on {date} at {time}";
FusionRoom.Help.InputSig.StringValue = requestString;
this.LogInformation("Help request sent to Fusion from room '{0}'", Room.Name);
this.LogDebug("Help request content: {0}", FusionRoom.Help.InputSig.StringValue);
_helpRequestSent = true;
HelpRequestSentFeedback.FireUpdate();
_helpRequestStatus = eFusionHelpResponse.HelpRequested;
HelpRequestStatusFeedback.FireUpdate();
}
/// <inheritdoc />
public void CancelHelpRequest()
{
if (_helpRequestSent)
{
FusionRoom.Help.InputSig.StringValue = "";
_helpRequestSent = false;
HelpRequestSentFeedback.FireUpdate();
_helpRequestStatus = eFusionHelpResponse.None;
HelpRequestStatusFeedback.FireUpdate();
Debug.LogMessage(LogEventLevel.Information, this, "Help request cancelled in Fusion for room '{0}'", Room.Name);
}
}
/// <inheritdoc />
public void ToggleHelpRequest()
{
if (_helpRequestSent)
{
CancelHelpRequest();
}
else
{
SendHelpRequest();
}
}
} }
/// <summary>
/// Extensions to enhance Fusion room, asset and signal creation.
/// </summary>
public static class FusionRoomExtensions public static class FusionRoomExtensions
{ {
/// <summary> /// <summary>
@@ -1648,6 +1945,8 @@ namespace PepperDash.Essentials.Core.Fusion
/// </summary> /// </summary>
public static BooleanSigData CreateOffsetBoolSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) public static BooleanSigData CreateOffsetBoolSig(this FusionRoom fr, uint number, string name, eSigIoMask mask)
{ {
Debug.LogDebug("Creating Offset Bool Sig: {0} at Join {1}", name, number);
if (number < 50) if (number < 50)
{ {
throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); throw new ArgumentOutOfRangeException("number", "Cannot be less than 50");
@@ -1668,6 +1967,8 @@ namespace PepperDash.Essentials.Core.Fusion
/// </summary> /// </summary>
public static UShortSigData CreateOffsetUshortSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) public static UShortSigData CreateOffsetUshortSig(this FusionRoom fr, uint number, string name, eSigIoMask mask)
{ {
Debug.LogDebug("Creating Offset UShort Sig: {0} at Join {1}", name, number);
if (number < 50) if (number < 50)
{ {
throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); throw new ArgumentOutOfRangeException("number", "Cannot be less than 50");
@@ -1688,6 +1989,8 @@ namespace PepperDash.Essentials.Core.Fusion
/// </summary> /// </summary>
public static StringSigData CreateOffsetStringSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) public static StringSigData CreateOffsetStringSig(this FusionRoom fr, uint number, string name, eSigIoMask mask)
{ {
Debug.LogDebug("Creating Offset String Sig: {0} at Join {1}", name, number);
if (number < 50) if (number < 50)
{ {
throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); throw new ArgumentOutOfRangeException("number", "Cannot be less than 50");
@@ -1803,6 +2106,9 @@ namespace PepperDash.Essentials.Core.Fusion
/// </summary> /// </summary>
public class RoomInformation public class RoomInformation
{ {
/// <summary>
/// Constructor
/// </summary>
public RoomInformation() public RoomInformation()
{ {
FusionCustomProperties = new List<FusionCustomProperty>(); FusionCustomProperties = new List<FusionCustomProperty>();
@@ -1855,10 +2161,17 @@ namespace PepperDash.Essentials.Core.Fusion
/// </summary> /// </summary>
public class FusionCustomProperty public class FusionCustomProperty
{ {
/// <summary>
/// Constructor
/// </summary>
public FusionCustomProperty() public FusionCustomProperty()
{ {
} }
/// <summary>
/// Constructor with id
/// </summary>
/// <param name="id"></param>
public FusionCustomProperty(string id) public FusionCustomProperty(string id)
{ {
ID = id; ID = id;

View File

@@ -0,0 +1,33 @@
using System.Collections.Generic;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Fusion;
/// <summary>
/// Factory for creating IEssentialsRoomFusionController devices
/// </summary>
public class IEssentialsRoomFusionControllerFactory : EssentialsDeviceFactory<IEssentialsRoomFusionController>
{
/// <summary>
/// Constructor
/// </summary>
public IEssentialsRoomFusionControllerFactory()
{
TypeNames = new List<string>() { "fusionRoom" };
}
/// <summary>
/// Builds the device
/// </summary>
/// <param name="dc"></param>
/// <returns></returns>
public override EssentialsDevice BuildDevice(PepperDash.Essentials.Core.Config.DeviceConfig dc)
{
Debug.LogDebug("Factory Attempting to create new IEssentialsRoomFusionController Device");
var properties = dc.Properties.ToObject<IEssentialsRoomFusionControllerPropertiesConfig>();
return new IEssentialsRoomFusionController(dc.Key, dc.Name, properties);
}
}

View File

@@ -0,0 +1,59 @@
using Newtonsoft.Json;
using PepperDash.Core;
/// <summary>
/// Config properties for an IEssentialsRoomFusionController device
/// </summary>
public class IEssentialsRoomFusionControllerPropertiesConfig
{
/// <summary>
/// Gets or sets the IP ID of the Fusion Room Controller
/// </summary>
[JsonProperty("ipId")]
public string IpId { get; set; }
/// <summary>
/// Gets the IP ID as a UInt16
/// </summary>
[JsonIgnore]
public uint IpIdInt
{
get
{
// Try to parse the IpId string to UInt16 as hex
if (ushort.TryParse(IpId, System.Globalization.NumberStyles.HexNumber, null, out ushort result))
{
return result;
}
else
{
Debug.LogWarning( "Failed to parse IpId '{0}' as UInt16", IpId);
return 0;
}
}
}
/// <summary>
/// Gets or sets the join map key
/// </summary>
[JsonProperty("joinMapKey")]
public string JoinMapKey { get; set; }
/// <summary>
/// Gets or sets the room key associated with this Fusion Room Controller
/// </summary>
[JsonProperty("roomKey")]
public string RoomKey { get; set; }
/// <summary>
/// Gets or sets whether to use HTML format for help requests
/// </summary>
[JsonProperty("useHtmlFormatForHelpRequests")]
public bool UseHtmlFormatForHelpRequests { get; set; } = false;
/// <summary>
/// Gets or sets whether to use 24-hour time format
/// </summary>
[JsonProperty("use24HourTimeFormat")]
public bool Use24HourTimeFormat { get; set; } = false;
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Core.Fusion
{
/// <summary>
/// Represents Fusion Help Request functionality
/// </summary>
public interface IFusionHelpRequest
{
/// <summary>
/// Feedback containing the response to a help request
/// </summary>
StringFeedback HelpRequestResponseFeedback { get; }
/// <summary>
/// Indicates whether a help request has been sent
/// </summary>
BoolFeedback HelpRequestSentFeedback { get; }
/// <summary>
/// Feedback containing the current status of the help request
/// </summary>
StringFeedback HelpRequestStatusFeedback { get; }
/// <summary>
/// Sends a help request
/// </summary>
void SendHelpRequest();
/// <summary>
/// Clears the current help request status
/// </summary>
void CancelHelpRequest();
/// <summary>
/// Toggles between sending and cancelling a help request
/// </summary>
void ToggleHelpRequest();
}
}

View File

@@ -0,0 +1,37 @@
namespace PepperDash.Essentials.Core.Fusion
{
/// <summary>
/// Enumeration of possible Fusion Help Responses based on the standard responses from Fusion
/// </summary>
public enum eFusionHelpResponse
{
/// <summary>
/// No help response
/// </summary>
None,
/// <summary>
/// Help has been requested
/// </summary>
HelpRequested,
/// <summary>
/// Help is on the way
/// </summary>
HelpOnTheWay,
/// <summary>
/// Please call the helpdesk.
/// </summary>
CallHelpDesk,
/// <summary>
/// Rescheduling meeting.
/// </summary>
ReschedulingMeeting,
/// <summary>
/// Technician taking control.
/// </summary>
TakingControl,
}
}

View File

@@ -49,7 +49,7 @@ namespace PepperDash.Essentials.Core
/// </summary> /// </summary>
public IRoomOccupancy Room { get; private set; } public IRoomOccupancy Room { get; private set; }
private Fusion.EssentialsHuddleSpaceFusionSystemControllerBase FusionRoom; private Fusion.IEssentialsRoomFusionController FusionRoom;
public RoomOnToDefaultSourceWhenOccupied(DeviceConfig config) : public RoomOnToDefaultSourceWhenOccupied(DeviceConfig config) :
base (config) base (config)
@@ -74,7 +74,7 @@ namespace PepperDash.Essentials.Core
var fusionRoomKey = PropertiesConfig.RoomKey + "-fusion"; var fusionRoomKey = PropertiesConfig.RoomKey + "-fusion";
FusionRoom = DeviceManager.GetDeviceForKey(fusionRoomKey) as Core.Fusion.EssentialsHuddleSpaceFusionSystemControllerBase; FusionRoom = DeviceManager.GetDeviceForKey(fusionRoomKey) as Core.Fusion.IEssentialsRoomFusionController;
if (FusionRoom == null) if (FusionRoom == null)
Debug.LogMessage(LogEventLevel.Debug, this, "Unable to get Fusion Room from Device Manager with key: {0}", fusionRoomKey); Debug.LogMessage(LogEventLevel.Debug, this, "Unable to get Fusion Room from Device Manager with key: {0}", fusionRoomKey);

View File

@@ -408,7 +408,7 @@ namespace PepperDash.Essentials.Core
Debug.LogMessage(LogEventLevel.Information, this, "Timeout Minutes from Config is: {0}", timeoutMinutes); Debug.LogMessage(LogEventLevel.Information, this, "Timeout Minutes from Config is: {0}", timeoutMinutes);
// If status provider is fusion, set flag to remote // If status provider is fusion, set flag to remote
if (statusProvider is Core.Fusion.EssentialsHuddleSpaceFusionSystemControllerBase) if (statusProvider is Core.Fusion.IEssentialsRoomFusionController)
OccupancyStatusProviderIsRemote = true; OccupancyStatusProviderIsRemote = true;
if(timeoutMinutes > 0) if(timeoutMinutes > 0)

View File

@@ -1,20 +1,22 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
using Crestron.SimplSharpPro.DeviceSupport;
using PepperDash.Core;
using Crestron.SimplSharpPro.UI;
using Crestron.SimplSharp.CrestronIO; using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharpPro; using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport;
using PepperDash.Core;
using PepperDash.Core.Logging;
using Serilog.Events; using Serilog.Events;
namespace PepperDash.Essentials.Core.UI namespace PepperDash.Essentials.Core.UI
{ {
public abstract class TouchpanelBase: EssentialsDevice, IHasBasicTriListWithSmartObject /// <summary>
/// Base class for Touchpanel devices
/// </summary>
public abstract class TouchpanelBase : EssentialsDevice, IHasBasicTriListWithSmartObject
{ {
/// <summary>
/// Gets or sets the configuration for the Crestron touchpanel.
/// </summary>
protected CrestronTouchpanelPropertiesConfig _config; protected CrestronTouchpanelPropertiesConfig _config;
/// <summary> /// <summary>
/// Gets or sets the Panel /// Gets or sets the Panel
@@ -27,12 +29,11 @@ namespace PepperDash.Essentials.Core.UI
/// is provided. /// is provided.
/// </summary> /// </summary>
/// <param name="key">Essentials Device Key</param> /// <param name="key">Essentials Device Key</param>
/// <param name="name">Essentials Device Name</param> /// <param name="name">Essentials Device Name</param>
/// <param name="type">Touchpanel Type to build</param> /// <param name="panel">Crestron Touchpanel Device</param>
/// <param name="config">Touchpanel Configuration</param> /// <param name="config">Touchpanel Configuration</param>
/// <param name="id">IP-ID to use for touch panel</param>
protected TouchpanelBase(string key, string name, BasicTriListWithSmartObject panel, CrestronTouchpanelPropertiesConfig config) protected TouchpanelBase(string key, string name, BasicTriListWithSmartObject panel, CrestronTouchpanelPropertiesConfig config)
:base(key, name) : base(key, name)
{ {
if (panel == null) if (panel == null)
@@ -55,23 +56,21 @@ namespace PepperDash.Essentials.Core.UI
tsw.ButtonStateChange += Tsw_ButtonStateChange; tsw.ButtonStateChange += Tsw_ButtonStateChange;
} }
_config = config; _config = config;
AddPreActivationAction(() => {
if (Panel.Register() != eDeviceRegistrationUnRegistrationResponse.Success)
Debug.LogMessage(LogEventLevel.Information, this, "WARNING: Registration failed. Continuing, but panel may not function: {0}", Panel.RegistrationFailureReason);
AddPreActivationAction(() =>
{
// Give up cleanly if SGD is not present. // Give up cleanly if SGD is not present.
var sgdName = Global.FilePathPrefix + "sgd" + Global.DirectorySeparator + _config.SgdFile; var sgdName = Global.FilePathPrefix + "sgd" + Global.DirectorySeparator + _config.SgdFile;
if (!File.Exists(sgdName)) if (!File.Exists(sgdName))
{ {
Debug.LogMessage(LogEventLevel.Information, this, "Smart object file '{0}' not present in User folder. Looking for embedded file", sgdName); this.LogInformation("Smart object file '{0}' not present in User folder. Looking for embedded file", sgdName);
sgdName = Global.ApplicationDirectoryPathPrefix + Global.DirectorySeparator + "SGD" + Global.DirectorySeparator + _config.SgdFile; sgdName = Global.ApplicationDirectoryPathPrefix + Global.DirectorySeparator + "SGD" + Global.DirectorySeparator + _config.SgdFile;
if (!File.Exists(sgdName)) if (!File.Exists(sgdName))
{ {
Debug.LogMessage(LogEventLevel.Information, this, "Unable to find SGD file '{0}' in User sgd or application SGD folder. Exiting touchpanel load.", sgdName); this.LogWarning("Unable to find SGD file '{0}' in User sgd or application SGD folder. Exiting touchpanel load.", sgdName);
return; return;
} }
} }
@@ -82,12 +81,11 @@ namespace PepperDash.Essentials.Core.UI
AddPostActivationAction(() => AddPostActivationAction(() =>
{ {
// Check for IEssentialsRoomCombiner in DeviceManager and if found, subscribe to its event // Check for IEssentialsRoomCombiner in DeviceManager and if found, subscribe to its event
var roomCombiner = DeviceManager.AllDevices.FirstOrDefault((d) => d is IEssentialsRoomCombiner) as IEssentialsRoomCombiner;
if (roomCombiner != null) if (DeviceManager.AllDevices.FirstOrDefault((d) => d is IEssentialsRoomCombiner) is IEssentialsRoomCombiner roomCombiner)
{ {
// Subscribe to the even // Subscribe to the even
roomCombiner.RoomCombinationScenarioChanged += new EventHandler<EventArgs>(roomCombiner_RoomCombinationScenarioChanged); roomCombiner.RoomCombinationScenarioChanged += new EventHandler<EventArgs>(RoomCombiner_RoomCombinationScenarioChanged);
// Connect to the initial roomKey // Connect to the initial roomKey
if (roomCombiner.CurrentScenario != null) if (roomCombiner.CurrentScenario != null)
@@ -106,6 +104,11 @@ namespace PepperDash.Essentials.Core.UI
// No room combiner, use the default key // No room combiner, use the default key
SetupPanelDrivers(_config.DefaultRoomKey); SetupPanelDrivers(_config.DefaultRoomKey);
} }
var panelRegistrationResponse = Panel.Register();
if (panelRegistrationResponse != eDeviceRegistrationUnRegistrationResponse.Success)
this.LogInformation("WARNING: Registration failed. Continuing, but panel may not function: {0}", Panel.RegistrationFailureReason);
}); });
} }
@@ -115,7 +118,6 @@ namespace PepperDash.Essentials.Core.UI
/// <param name="roomKey">Room Key for this panel</param> /// <param name="roomKey">Room Key for this panel</param>
protected abstract void SetupPanelDrivers(string roomKey); protected abstract void SetupPanelDrivers(string roomKey);
/// <summary> /// <summary>
/// Event handler for System Extender Events /// Event handler for System Extender Events
/// </summary> /// </summary>
@@ -129,7 +131,7 @@ namespace PepperDash.Essentials.Core.UI
/// </summary> /// </summary>
/// <param name="sender"></param> /// <param name="sender"></param>
/// <param name="e"></param> /// <param name="e"></param>
protected virtual void roomCombiner_RoomCombinationScenarioChanged(object sender, EventArgs e) protected virtual void RoomCombiner_RoomCombinationScenarioChanged(object sender, EventArgs e)
{ {
var roomCombiner = sender as IEssentialsRoomCombiner; var roomCombiner = sender as IEssentialsRoomCombiner;
@@ -156,23 +158,23 @@ namespace PepperDash.Essentials.Core.UI
SetupPanelDrivers(newRoomKey); SetupPanelDrivers(newRoomKey);
} }
private void Panel_SigChange(object currentDevice, Crestron.SimplSharpPro.SigEventArgs args) private void Panel_SigChange(object currentDevice, SigEventArgs args)
{ {
Debug.LogMessage(LogEventLevel.Verbose, this, "Sig change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue); this.LogVerbose("Sig change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue);
var uo = args.Sig.UserObject; var uo = args.Sig.UserObject;
if (uo is Action<bool>) if (uo is Action<bool>)
(uo as Action<bool>)(args.Sig.BoolValue); (uo as Action<bool>)(args.Sig.BoolValue);
else if (uo is Action<ushort>) else if (uo is Action<ushort>)
(uo as Action<ushort>)(args.Sig.UShortValue); (uo as Action<ushort>)(args.Sig.UShortValue);
else if (uo is Action<string>) else if (uo is Action<string>)
(uo as Action<string>)(args.Sig.StringValue); (uo as Action<string>)(args.Sig.StringValue);
} }
private void Tsw_ButtonStateChange(GenericBase device, ButtonEventArgs args) private void Tsw_ButtonStateChange(GenericBase device, ButtonEventArgs args)
{ {
var uo = args.Button.UserObject; var uo = args.Button.UserObject;
if(uo is Action<bool>) if (uo is Action<bool>)
(uo as Action<bool>)(args.Button.State == eButtonState.Pressed); (uo as Action<bool>)(args.Button.State == eButtonState.Pressed);
} }
} }
} }

View File

@@ -2174,6 +2174,7 @@ namespace PepperDash.Essentials
{ {
var clientId = content["clientId"].Value<string>(); var clientId = content["clientId"].Value<string>();
var roomKey = content["roomKey"].Value<string>(); var roomKey = content["roomKey"].Value<string>();
var touchpanelKey = content.SelectToken("touchpanelKey");
if (_roomCombiner == null) if (_roomCombiner == null)
{ {
@@ -2187,6 +2188,8 @@ namespace PepperDash.Essentials
SendMessageObject(message); SendMessageObject(message);
SendDeviceInterfaces(clientId); SendDeviceInterfaces(clientId);
SendTouchpanelKey(clientId, touchpanelKey);
return; return;
} }
@@ -2202,6 +2205,8 @@ namespace PepperDash.Essentials
SendMessageObject(message); SendMessageObject(message);
SendDeviceInterfaces(clientId); SendDeviceInterfaces(clientId);
SendTouchpanelKey(clientId, touchpanelKey);
return; return;
} }
@@ -2221,6 +2226,8 @@ namespace PepperDash.Essentials
SendMessageObject(message); SendMessageObject(message);
SendDeviceInterfaces(clientId); SendDeviceInterfaces(clientId);
SendTouchpanelKey(clientId, touchpanelKey);
return; return;
} }
@@ -2236,6 +2243,24 @@ namespace PepperDash.Essentials
SendMessageObject(newMessage); SendMessageObject(newMessage);
SendDeviceInterfaces(clientId); SendDeviceInterfaces(clientId);
SendTouchpanelKey(clientId, touchpanelKey);
}
private void SendTouchpanelKey(string clientId, JToken touchpanelKeyToken)
{
if (touchpanelKeyToken == null)
{
this.LogWarning("Touchpanel key not found for client {clientId}", clientId);
return;
}
SendMessageObject(new MobileControlMessage
{
Type = "/system/touchpanelKey",
ClientId = clientId,
Content = touchpanelKeyToken.Value<string>()
});
} }
private void SendDeviceInterfaces(string clientId) private void SendDeviceInterfaces(string clientId)
@@ -2374,7 +2399,7 @@ namespace PepperDash.Essentials
foreach (var handler in handlers) foreach (var handler in handlers)
{ {
Task.Run(async () => Task.Run(() =>
{ {
try try
{ {

View File

@@ -33,7 +33,7 @@ namespace PepperDash.Essentials.Touchpanel
return; return;
} }
AddAction($"/fullStatus", (id, context) => SendFullStatus()); AddAction($"/fullStatus", (id, context) => SendFullStatus(id));
AddAction($"/openApp", (id, context) => _appControl.OpenApp()); AddAction($"/openApp", (id, context) => _appControl.OpenApp());

View File

@@ -252,6 +252,7 @@ namespace PepperDash.Essentials.Touchpanel
if (!x70Panel.ExtenderApplicationControlReservedSigs.HideOpenedApplicationFeedback.BoolValue) if (!x70Panel.ExtenderApplicationControlReservedSigs.HideOpenedApplicationFeedback.BoolValue)
{ {
x70Panel.ExtenderButtonToolbarReservedSigs.ShowButtonToolbar(); x70Panel.ExtenderButtonToolbarReservedSigs.ShowButtonToolbar();
x70Panel.ExtenderButtonToolbarReservedSigs.Button2On(); x70Panel.ExtenderButtonToolbarReservedSigs.Button2On();
} }
else else
@@ -294,17 +295,16 @@ namespace PepperDash.Essentials.Touchpanel
handler(this, new DeviceInfoEventArgs(DeviceInfo)); handler(this, new DeviceInfoEventArgs(DeviceInfo));
}; };
x70Panel.ExtenderButtonToolbarReservedSigs.DeviceExtenderSigChange += (o, a) =>
{
this.LogVerbose("X70 Button Toolbar Device Extender args: {event}:{sig}:{name}:{type}:{boolValue}:{ushortValue}:{stringValue}", a.Event, a.Sig, a.Sig.Name, a.Sig.Type, a.Sig.BoolValue, a.Sig.UShortValue, a.Sig.StringValue);
};
x70Panel.ExtenderApplicationControlReservedSigs.Use(); x70Panel.ExtenderApplicationControlReservedSigs.Use();
x70Panel.ExtenderZoomRoomAppReservedSigs.Use(); x70Panel.ExtenderZoomRoomAppReservedSigs.Use();
x70Panel.ExtenderEthernetReservedSigs.Use(); x70Panel.ExtenderEthernetReservedSigs.Use();
x70Panel.ExtenderButtonToolbarReservedSigs.Use(); x70Panel.ExtenderButtonToolbarReservedSigs.Use();
x70Panel.ExtenderButtonToolbarReservedSigs.Button1Off();
x70Panel.ExtenderButtonToolbarReservedSigs.Button3Off();
x70Panel.ExtenderButtonToolbarReservedSigs.Button4Off();
x70Panel.ExtenderButtonToolbarReservedSigs.Button5Off();
x70Panel.ExtenderButtonToolbarReservedSigs.Button6Off();
return; return;
} }
@@ -414,34 +414,79 @@ namespace PepperDash.Essentials.Touchpanel
McServerUrlFeedback.LinkInputSig(Panel.StringInput[3]); McServerUrlFeedback.LinkInputSig(Panel.StringInput[3]);
UserCodeFeedback.LinkInputSig(Panel.StringInput[4]); UserCodeFeedback.LinkInputSig(Panel.StringInput[4]);
Panel.IpInformationChange += (sender, args) => Panel.IpInformationChange -= Panel_IpInformationChange;
Panel.IpInformationChange += Panel_IpInformationChange;
Panel.OnlineStatusChange -= Panel_OnlineChange;
Panel.OnlineStatusChange += Panel_OnlineChange;
}
private void Panel_OnlineChange(GenericBase sender, OnlineOfflineEventArgs args)
{
try
{ {
if (args.Connected) if (!args.DeviceOnLine)
{ {
this.LogVerbose("Connection from IP: {ip}", args.DeviceIpAddress); this.LogInformation("panel is offline");
this.LogInformation("Sending {appUrl} on join 1", AppUrlFeedback.StringValue); return;
var appUrl = GetUrlWithCorrectIp(_appUrl);
Panel.StringInput[1].StringValue = appUrl;
SetAppUrl(appUrl);
} }
else
{
this.LogVerbose("Disconnection from IP: {ip}", args.DeviceIpAddress);
}
};
Panel.OnlineStatusChange += (sender, args) => this.LogDebug("panel is online");
{
this.LogInformation("Sending {appUrl} on join 1", AppUrlFeedback.StringValue);
UpdateFeedbacks(); UpdateFeedbacks();
Panel.StringInput[1].StringValue = _appUrl; Panel.StringInput[1].StringValue = _appUrl;
Panel.StringInput[2].StringValue = QrCodeUrlFeedback.StringValue; Panel.StringInput[2].StringValue = QrCodeUrlFeedback.StringValue;
Panel.StringInput[3].StringValue = McServerUrlFeedback.StringValue; Panel.StringInput[3].StringValue = McServerUrlFeedback.StringValue;
Panel.StringInput[4].StringValue = UserCodeFeedback.StringValue; Panel.StringInput[4].StringValue = UserCodeFeedback.StringValue;
};
if (Panel is TswXX70Base x70Panel)
{
this.LogDebug("setting buttons off");
x70Panel.ExtenderButtonToolbarReservedSigs.Button1Off();
x70Panel.ExtenderButtonToolbarReservedSigs.Button3Off();
x70Panel.ExtenderButtonToolbarReservedSigs.Button4Off();
x70Panel.ExtenderButtonToolbarReservedSigs.Button5Off();
x70Panel.ExtenderButtonToolbarReservedSigs.Button6Off();
}
SendUrlToPanel();
}
catch (Exception ex)
{
this.LogError("Exception in panel online: {message}", ex.Message);
this.LogDebug(ex, "Stack Trace: ");
}
}
private void SendUrlToPanel()
{
var appUrl = GetUrlWithCorrectIp(_appUrl);
this.LogInformation("Sending {appUrl} on join 1", AppUrlFeedback.StringValue);
if (Panel.StringInput[1].StringValue == appUrl)
{
this.LogInformation("App URL already set to {appUrl}, no update needed", AppUrlFeedback.StringValue);
return;
}
Panel.StringInput[1].StringValue = appUrl;
SetAppUrl(appUrl);
}
private void Panel_IpInformationChange(GenericBase sender, ConnectedIpEventArgs args)
{
if (args.Connected)
{
this.LogVerbose("Connection from IP: {ip}", args.DeviceIpAddress);
SendUrlToPanel();
}
else
{
this.LogVerbose("Disconnection from IP: {ip}", args.DeviceIpAddress);
}
} }
/// <summary> /// <summary>

View File

@@ -1,6 +1,7 @@
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.AppServer; using PepperDash.Essentials.AppServer;
using PepperDash.Essentials.AppServer.Messengers; using PepperDash.Essentials.AppServer.Messengers;
@@ -29,17 +30,17 @@ namespace PepperDash.Essentials.Touchpanel
{ {
AddAction("/fullStatus", (id, content) => AddAction("/fullStatus", (id, content) =>
{ {
PostStatusMessage(new ThemeUpdateMessage { Theme = _tpDevice.Theme }); PostStatusMessage(new ThemeUpdateMessage { Theme = _tpDevice.Theme }, id);
}); });
AddAction("/saveTheme", (id, content) => AddAction("/saveTheme", (id, content) =>
{ {
var theme = content.ToObject<MobileControlSimpleContent<string>>(); var theme = content.ToObject<MobileControlSimpleContent<string>>();
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "Setting theme to {theme}", this, theme.Value); this.LogInformation("Setting theme to {theme}", theme.Value);
_tpDevice.UpdateTheme(theme.Value); _tpDevice.UpdateTheme(theme.Value);
PostStatusMessage(JToken.FromObject(new { theme = theme.Value })); PostStatusMessage(JToken.FromObject(new { theme = theme.Value }), id);
}); });
} }
} }

View File

@@ -385,9 +385,9 @@ namespace PepperDash.Essentials.WebSocketServer
var appUrl = $"http://{ip}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}"; var appUrl = $"http://{ip}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}";
this.LogVerbose("Sending URL {appUrl}", appUrl); this.LogVerbose("Sending URL {appUrl} to touchpanel {touchpanelKey}", appUrl, touchpanel.Touchpanel.Key);
touchpanel.Messenger.UpdateAppUrl($"http://{ip}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}"); touchpanel.Touchpanel.SetAppUrl($"http://{ip}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}");
} }
} }
@@ -723,7 +723,7 @@ namespace PepperDash.Essentials.WebSocketServer
private UiClient BuildUiClient(string roomKey, JoinToken token, string key) private UiClient BuildUiClient(string roomKey, JoinToken token, string key)
{ {
var c = new UiClient($"uiclient-{key}-{roomKey}-{token.Id}", token.Id, token.Token); var c = new UiClient($"uiclient-{key}-{roomKey}-{token.Id}", token.Id, token.Token, token.TouchpanelKey);
this.LogInformation("Constructing UiClient with key {key} and ID {id}", key, token.Id); this.LogInformation("Constructing UiClient with key {key} and ID {id}", key, token.Id);
c.Controller = _parent; c.Controller = _parent;
c.RoomKey = roomKey; c.RoomKey = roomKey;

View File

@@ -31,6 +31,11 @@ namespace PepperDash.Essentials.WebSocketServer
/// </summary> /// </summary>
public string Token { get; private set; } public string Token { get; private set; }
/// <summary>
/// Touchpanel Key associated with this client
/// </summary>
public string TouchpanelKey { get; private set; }
/// <summary> /// <summary>
/// Gets or sets the mobile control system controller that handles this client's messages /// Gets or sets the mobile control system controller that handles this client's messages
/// </summary> /// </summary>
@@ -75,11 +80,13 @@ namespace PepperDash.Essentials.WebSocketServer
/// <param name="key">The unique key to identify this client</param> /// <param name="key">The unique key to identify this client</param>
/// <param name="id">The client ID used by the client for this connection</param> /// <param name="id">The client ID used by the client for this connection</param>
/// <param name="token">The token associated with this client</param> /// <param name="token">The token associated with this client</param>
public UiClient(string key, string id, string token) /// <param name="touchpanelKey">The touchpanel key associated with this client</param>
public UiClient(string key, string id, string token, string touchpanelKey = "")
{ {
Key = key; Key = key;
Id = id; Id = id;
Token = token; Token = token;
TouchpanelKey = touchpanelKey;
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -105,6 +112,7 @@ namespace PepperDash.Essentials.WebSocketServer
{ {
clientId = Id, clientId = Id,
roomKey = RoomKey, roomKey = RoomKey,
touchpanelKey = TouchpanelKey ?? string.Empty,
}) })
}; };