using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core; using PepperDash.Core.Logging; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.DeviceTypeInterfaces; using System; using System.Collections.Generic; using System.Linq; namespace PepperDash.Essentials.AppServer.Messengers { /// /// Provides a messaging bridge /// public abstract class MessengerBase : EssentialsDevice, IMobileControlMessenger { protected IKeyName _device; private readonly List _deviceInterfaces; private readonly Dictionary> _actions = new Dictionary>(); public string DeviceKey => _device?.Key ?? ""; /// /// /// public IMobileControl AppServerController { get; private set; } public string MessagePath { get; private set; } /// /// /// /// /// protected MessengerBase(string key, string messagePath) : base(key) { Key = key; if (string.IsNullOrEmpty(messagePath)) throw new ArgumentException("messagePath must not be empty or null"); MessagePath = messagePath; } protected MessengerBase(string key, string messagePath, IKeyName device) : this(key, messagePath) { _device = device; _deviceInterfaces = GetInterfaces(_device as Device); } /// /// Gets the interfaces implmented on the device /// /// /// private List GetInterfaces(Device device) { return device?.GetType().GetInterfaces().Select((i) => i.Name).ToList() ?? new List(); } /// /// Registers this messenger with appserver controller /// /// public void RegisterWithAppServer(IMobileControl appServerController) { AppServerController = appServerController ?? throw new ArgumentNullException("appServerController"); AppServerController.AddAction(this, HandleMessage); RegisterActions(); } private void HandleMessage(string path, string id, JToken content) { // replace base path with empty string. Should leave something like /fullStatus var route = path.Replace(MessagePath, string.Empty); if (!_actions.TryGetValue(route, out var action)) { return; } Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Executing action for path {path}", this, path); action(id, content); } protected void AddAction(string path, Action action) { if (_actions.ContainsKey(path)) { //Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"Messenger {Key} already has action registered at {path}", this); return; } _actions.Add(path, action); } public List GetActionPaths() { return _actions.Keys.ToList(); } protected void RemoveAction(string path) { if (!_actions.ContainsKey(path)) { return; } _actions.Remove(path); } /// /// Implemented in extending classes. Wire up API calls and feedback here /// /// protected virtual void RegisterActions() { } /// /// Helper for posting status message /// /// /// protected void PostStatusMessage(DeviceStateMessageBase message, string clientId = null) { try { if (message == null) { throw new ArgumentNullException("message"); } if (_device == null) { throw new ArgumentNullException("device"); } message.SetInterfaces(_deviceInterfaces); message.Key = _device.Key; message.Name = _device.Name; var token = JToken.FromObject(message); PostStatusMessage(token, MessagePath, clientId); } catch (Exception ex) { this.LogError(ex, "Exception posting status message for {messagePath} to {clientId}", MessagePath, clientId ?? "all clients"); } } protected void PostStatusMessage(string type, DeviceStateMessageBase deviceState, string clientId = null) { try { //Debug.Console(2, this, "*********************Setting DeviceStateMessageProperties on MobileControlResponseMessage"); deviceState.SetInterfaces(_deviceInterfaces); deviceState.Key = _device.Key; deviceState.Name = _device.Name; deviceState.MessageBasePath = MessagePath; var token = JToken.FromObject(deviceState); PostStatusMessage(token, type, clientId); } catch (Exception ex) { this.LogError(ex, "Exception posting status message for {type} to {clientId}", type, clientId ?? "all clients"); } } protected void PostStatusMessage(JToken content, string type = "", string clientId = null) { try { AppServerController?.SendMessageObject(new MobileControlMessage { Type = !string.IsNullOrEmpty(type) ? type : MessagePath, ClientId = clientId, Content = content }); } catch (Exception ex) { Debug.LogMessage(ex, "Exception posting status message", this); } } protected void PostEventMessage(DeviceEventMessageBase message) { message.Key = _device.Key; message.Name = _device.Name; AppServerController?.SendMessageObject(new MobileControlMessage { Type = $"/event{MessagePath}/{message.EventType}", Content = JToken.FromObject(message), }); } protected void PostEventMessage(DeviceEventMessageBase message, string eventType) { message.Key = _device.Key; message.Name = _device.Name; message.EventType = eventType; AppServerController?.SendMessageObject(new MobileControlMessage { Type = $"/event{MessagePath}/{eventType}", Content = JToken.FromObject(message), }); } protected void PostEventMessage(string eventType) { AppServerController?.SendMessageObject(new MobileControlMessage { Type = $"/event{MessagePath}/{eventType}", Content = JToken.FromObject(new { }), }); } } public abstract class DeviceMessageBase { /// /// The device key /// [JsonProperty("key")] public string Key { get; set; } /// /// The device name /// [JsonProperty("name")] public string Name { get; set; } /// /// The type of the message class /// [JsonProperty("messageType")] public string MessageType => GetType().Name; [JsonProperty("messageBasePath")] public string MessageBasePath { get; set; } } /// /// Base class for state messages that includes the type of message and the implmented interfaces /// public class DeviceStateMessageBase : DeviceMessageBase { /// /// The interfaces implmented by the device sending the messsage /// [JsonProperty("interfaces")] public List Interfaces { get; private set; } public void SetInterfaces(List interfaces) { Interfaces = interfaces; } } /// /// Base class for event messages that include the type of message and an event type /// public abstract class DeviceEventMessageBase : DeviceMessageBase { /// /// The event type /// [JsonProperty("eventType")] public string EventType { get; set; } } }