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; }
}
}