//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Tango Observables Generator
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated. Do not modify!
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Xml.Serialization;
using Newtonsoft.Json;
using System.Linq;
using Tango.DAL.Remote.DB;
using Tango.Core;
using System.ComponentModel;
using Tango.Core.CustomAttributes;
namespace Tango.BL.Entities
{
[Table("TANGO_UPDATES")]
public abstract class TangoUpdateBase : ObservableEntity<TangoUpdate>
{
public event EventHandler<String> ApplicationVersionChanged;
public event EventHandler<String> FirmwareVersionChanged;
public event EventHandler<Int32> StatusChanged;
public event EventHandler<String> FailedReasonChanged;
public event EventHandler<String> FailedLogChanged;
public event EventHandler<DateTime> StartDateChanged;
public event EventHandler<Nullable<DateTime>> EndDateChanged;
public event EventHandler<Boolean> IsSynchronizedChanged;
protected String _applicationversion;
/// <summary>
/// Gets or sets the tangoupdatebase application version.
/// </summary>
[Column("APPLICATION_VERSION")]
public String ApplicationVersion
{
get
{
return _applicationversion;
}
set
{
if (_applicationversion != value)
{
_applicationversion = value;
OnApplicationVersionChanged(value);
}
}
}
protected String _firmwareversion;
/// <summary>
/// Gets or sets the tangoupdatebase firmware version.
/// </summary>
[Column("FIRMWARE_VERSION")]
public String FirmwareVersion
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Google.Protobuf;
using Tango.BL.Entities;
using Tango.Integration.Operation;
using Tango.PMR;
using Tango.PMR.Common;
using Tango.Transport;
using Tango.Transport.Transporters;
using Tango.PMR.Integration;
using Tango.Transport.Discovery;
using Tango.Transport.Servers;
using Tango.Transport.Adapters;
using Tango.PMR.Connection;
using Tango.PMR.Diagnostics;
using Tango.PMR.Debugging;
using System.Security.Authentication;
using Tango.Settings;
using Tango.Core.ExtensionMethods;
using Tango.PMR.MachineStatus;
using Tango.PMR.Power;
using Tango.Core;
using Microsoft.AspNet.SignalR.Client;
using Tango.Integration.ExternalBridge.Web;
using Tango.Core.Threading;
using System.Diagnostics;
using System.Reflection;
using Newtonsoft.Json;
namespace Tango.Integration.ExternalBridge
{
public class ExternalBridgeService : ExtendedObject, IExternalBridgeService
{
private List<ExternalBridgeReceiver> _receivers;
private UdpDiscoveryService<ExternalBridgeUdpDiscoveryPacket> _discoveryService;
private TcpServer _tcpServer;
private int _discovery_port = 8888; //Will be overridden by settings in constructor..
private int _external_bridge_port = 1984; //Will be overridden by settings in constructor..
private HubConnection _connection;
private IHubProxy _proxy;
private bool _isSignalRConnected;
private System.Timers.Timer _signalrPingTimer;
private Dictionary<String, RequestHandler> _requestHandlers;
private static JsonSerializerSettings _genericMessageSettings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.All
};
private class RequestHandler
{
public IExternalBridgeRequestHandler Handler { get; set; }
public MethodInfo Method { get; set; }
public RequestHandlerLoggingMode LoggingMode { get; set; }
}
#region Events
/// <summary>
/// Occurs when a new client is waiting for authentication.
/// </summary>
public event EventHandler<ExternalBridgeClientConnectedEventArgs> ConnectionRequest;
/// <summary>
/// Occurs when the last client has been disconnected.
/// </summary>
public event EventHandler FullControlSessionDisconnected;
/// <summary>
/// Occurs when the service has received a new color profile request.
/// </summary>
public event EventHandler<ColorProfileRequestEventArgs> ColorProfileRequest;
#endregion
#region Properties
public static HashSet<MessageType> SafetyLevelOperations { get; private set; }
/// <summary>
/// Gets or sets the TCP transport adapter write mode when creating a TCP receiver.
/// </summary>
public TcpTransportAdapterWriteMode TcpTransportAdapterWriteMode { get; set; }
/// <summary>
/// Gets or sets the SignalR configuration.
/// </summary>
public ExternalBridgeSignalRConfiguration SignalRConfiguration { get; set; }
private IMachineOperator _machineOperator;
/// <summary>
/// Gets or sets the machine operator.
/// </summary>
public IMachineOperator MachineOperator
{
get { return _machineOperator; }
set { _machineOperator = value; OnMachineOperatorChanged(); }
}
private Machine _machine;
/// <summary>
/// Gets or sets the machine.
/// </summary>
public Machine Machine
{
get { return _machine; }
set { _machine = value; OnMachineChanged(); }
}
/// <summary>
/// Gets a value indicating whether this instance is started.
/// </summary>
public bool IsStarted { get; private set; }
private bool _enabled;
/// <summary>
/// Gets or sets a value indicating whether this <see cref="IExternalBridgeService" /> is enabled.
/// </summary>
public bool Enabled
{
get { return _enabled; }
set
{
_enabled = value;
if (_enabled)
{
Start();
}
else
{
Stop();
}
RaisePropertyChangedAuto();
}
}
private bool _hasSessions;
/// <summary>
/// Gets a value indicating whether there are any connected sessions.
/// </summary>
public bool HasSessions
{
get { return _hasSessions; }
private set { _hasSessions = value; RaisePropertyChangedAuto(); }
}
private ExternalBridgeReceiver _fullControlSessionReceiver;
/// <summary>
/// Gets the current full control session receiver.
/// </summary>
public ExternalBridgeReceiver FullControlSessionReceiver
{
get { return _fullControlSessionReceiver; }
private set { _fullControlSessionReceiver = value; RaisePropertyChangedAuto(); }
}
#endregion
#region Constructors
/// <summary>
/// Initializes the <see cref="ExternalBridgeService"/> class.
/// </summary>
static ExternalBridgeService()
{
SafetyLevelOperations = new HashSet<MessageType>();
}
/// <summary>
/// Initializes a new instance of the <see cref="ExternalBridgeService"/> class.
/// </summary>
public ExternalBridgeService()
{
_requestHandlers = new Dictionary<string, RequestHandler>();
SignalRConfiguration = new ExternalBridgeSignalRConfiguration();
TcpTransportAdapterWriteMode = TcpTransportAdapterWriteMode.Interval;
_receivers = new List<ExternalBridgeReceiver>();
var settings = SettingsManager.Default.GetOrCreate<IntegrationSettings>();
_discovery_port = settings.ExternalBridgeServiceDiscoveryPort;
_external_bridge_port = settings.ExternalBridgeServicePort;
_tcpServer = new TcpServer(_external_bridge_port);
_tcpServer.ClientConnected += _tcpServer_ClientConnected;
LogManager.NewLog += LogManager_NewLog;
}
/// <summary>
/// Initializes a new instance of the <see cref="ExternalBridgeService"/> class.
/// </summary>
/// <param name="machineOperator">The machine operator.</param>
/// <param name="machine">The machine.</param>
public ExternalBridgeService(IMachineOperator machineOperator, Machine machine) : this()
{
Machine = machine;
MachineOperator = machineOperator;
}
#endregion
#region Properties Changes
/// <summary>
/// Called when the machine has been changed
/// </summary>
protected virtual void OnMachineChanged()
{
if (_discoveryService != null && _discoveryService.IsStarted)
{
_discoveryService.Stop();
}
_discoveryService = new UdpDiscoveryService<ExternalBridgeUdpDiscoveryPacket>(_discovery_port, new ExternalBridgeUdpDiscoveryPacket()
{
SerialNumber = Machine.SerialNumber,
});
_discoveryService.BeforeBroadcasting -= _discoverySevice_BeforeBroadcasting;
_discoveryService.BeforeBroadcasting += _discoverySevice_BeforeBroadcasting;
}
private void _discoverySevice_BeforeBroadcasting(object sender, ExternalBridgeUdpDiscoveryPacket e)
{
e.Time = DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss");
}
/// <summary>
/// Called when the machine operator has been changed
/// </summary>
protected virtual void OnMachineOperatorChanged()
{
if (MachineOperator != null)
{
MachineOperator.StatusChanged -= MachineOperator_StatusChanged;
MachineOperator.StatusChanged += MachineOperator_StatusChanged;
MachineOperator.PendingResponseReceived -= MachineOperator_PendingResponseReceived;
MachineOperator.PendingResponseReceived += MachineOperator_PendingResponseReceived;
}
}
#endregion
#region Event Handlers
/// <summary>
/// Handles the ClientConnected event of the _tcpServer control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="ClientConnectedEventArgs"/> instance containing the event data.</param>
private async void _tcpServer_ClientConnected(object sender, ClientConnectedEventArgs e)
{
LogManager.Log("External bridge TCP client connected from: " + e.Socket.GetIPAddress());
ExternalBridgeReceiver receiver = new ExternalBridgeReceiver(e.Socket, MachineOperator);
(receiver.Adapter as TcpTransportAdapter).WriteMode = TcpTransportAdapterWriteMode;
receiver.LoginRequest += Receiver_LoginRequest;
receiver.ColorProfileRequest += Receiver_ColorProfileRequest;
receiver.Disconnected += Receiver_Disconnected;
receiver.ReceiverRequestReceived += Receiver_ReceiverRequestReceived;
_receivers.Add(receiver);
await receiver.Connect();
}
private async void OnSignalRSessionCreated(String sessionID)
{
try
{
LogManager.Log("External bridge SignalR client connected.");
var adapter = new SignalRTransportAdapter(SignalRConfiguration.Address, SignalRConfiguration.Hub, SignalRTransportAdapterMode.JoinSession, Machine.SerialNumber, sessionID); ;
ExternalBridgeReceiver receiver = new ExternalBridgeReceiver(adapter, MachineOperator);
receiver.LoginRequest += Receiver_LoginRequest;
receiver.ColorProfileRequest += Receiver_ColorProfileRequest;
receiver.Disconnected += Receiver_Disconnected;
receiver.ReceiverRequestReceived += Receiver_ReceiverRequestReceived;
_receivers.Add(receiver);
await receiver.Connect();
}
catch (Exception ex)
{
LogManager.Log(ex, "Error initializing SignalR ExternalBridgeReceiver when session created.");
}
}
private void MachineOperator_StatusChanged(object sender, MachineStatuses status)
{
try
{
_receivers.ToList().Where(x => x.IsLoggedInAndRequiresDiagnostics).ToList().ForEach(x => x.UpdateMachineOperatorStatus((UpdateStatus)status));
}
catch (Exception ex)
{
LogManager.Log(ex, "Error updating status of external bridge service client.");
}
}
private void LogManager_NewLog(object sender, Tango.Logging.LogItemBase e)
{
var toSend = _receivers.ToList().Where(x => x.IsLoggedInAndRequiresDiagnostics && x.RequiresApplicationLogs).ToList();
if (_receivers.Count > 0)
{
var msg = MessageFactory.CreateTangoMessage<StartApplicationLogsResponse>(new StartApplicationLogsResponse()
{
LogItem = ByteString.CopyFrom(e.Serialize())
});
msg.ToBytes();
toSend.ToList().Where(x => x.State == TransportComponentState.Connected).ToList().ForEach(x => x.UpdateApplicationLogs(msg.Container));
}
}
private void MachineOperator_PendingResponseReceived(object sender, MessageContainer container)
{
OnOperatorResponseReceived(container);
}
#endregion
#region Receiver Events Handlers
private void Receiver_Disconnected(object sender, EventArgs e)
{
var receiver = sender as ExternalBridgeReceiver;
receiver.LoginRequest -= Receiver_LoginRequest;
receiver.ColorProfileRequest -= Receiver_ColorProfileRequest;
receiver.Disconnected -= Receiver_Disconnected;
receiver.ReceiverRequestReceived -= Receiver_ReceiverRequestReceived;
_receivers.Remove(receiver);
if (receiver.LoginIntent == ExternalBridgeLoginIntent.FullControl)
{
FullControlSessionDisconnected?.Invoke(this, new EventArgs());
}
HasSessions = _receivers.Count(x => x.IsLoggedIn) > 0;
FullControlSessionReceiver = _receivers.SingleOrDefault(x => x.IsLoggedIn && x.LoginIntent == ExternalBridgeLoginIntent.FullControl);
//Notify request handlers about this receiver disconnected.
foreach (var requestHandler in _requestHandlers.ToList().Select(x => x.Value.Handler))
{
try
{
requestHandler.OnReceiverDisconnected(receiver);
}
catch { } //Ignore exceptions on handler.
}
}
private void Receiver_ColorProfileRequest(object sender, ColorProfileRequestEventArgs e)
{
ColorProfileRequest?.Invoke(this, e);
}
private void Receiver_LoginRequest(object sender, ExternalBridgeReceiverLoginRequestEventArgs e)
{
var request = e.Request;
if (request.Intent == ExternalBridgeLoginIntent.FullControl && _receivers.Any(x => x.IsLoggedIn && x.LoginIntent == ExternalBridgeLoginIntent.FullControl))
{
e.Decline("Only one full control client is allowed.");
return;
}
if (request.Intent == ExternalBridgeLoginIntent.FullControl && MachineOperator.IsPrinting)
{
e.Decline($"External bridge client login failed because the machine is currently printing and '{ExternalBridgeLoginIntent.FullControl}' intent was requested.");
return;
}
ExternalBridgeClientConnectedEventArgs args = null;
args = new ExternalBridgeClientConnectedEventArgs(() =>
{
//Confirmed
e.Confirm(Machine, MachineOperator.DeviceInformation, args.ApplicationInformation);
HasSessions = true;
if (request.Intent == ExternalBridgeLoginIntent.FullControl)
{
FullControlSessionReceiver = sender as ExternalBridgeReceiver;
}
}, (reason) =>
{
//Declined
e.Decline(reason);
});
args.Request = request;
args.Address = e.Address;
ConnectionRequest?.Invoke(this, args);
}
private void Receiver_ReceiverRequestReceived(object sender, ExternalBridgeReceiverRequestReceivedEventArgs e)
{
if (e.Container.Type == MessageType.GenericRequest || _requestHandlers.ContainsKey(e.Container.Type.ToString()))
{
e.Handled = true;
ThreadFactory.StartNew(() =>
{
try
{
var message = MessageFactory.ExtractMessageFromContainer(e.Container);
if (e.Container.Type != MessageType.GenericRequest) //Handle standard PMR messages.
{
var handler = _requestHandlers[e.Container.Type.ToString()];
if (handler.Method.ReturnType == typeof(Task))
{
((Task)handler.Method.Invoke(handler.Handler, new object[]
{
message,
e.Container.Token,
sender,
})).Wait();
}
else
{
handler.Method.Invoke(handler.Handler, new object[]
{
message,
e.Container.Token,
sender,
});
}
}
else //Handle GenericRequest with inner JSON formated generic message.
{
var genericType = Type.GetType((message as GenericRequest).Type);
try
{
if (_requestHandlers.ContainsKey(genericType.FullName))
{
var innerMessage = GenericMessageSerializer.DeserializeFromByteString(genericType, (message as GenericRequest).Data);
var handler = _requestHandlers[genericType.FullName];
if (handler.LoggingMode == RequestHandlerLoggingMode.LogRequestName || handler.LoggingMode == RequestHandlerLoggingMode.LogRequestNameAndContent)
{
String content = ".";
if (handler.LoggingMode == RequestHandlerLoggingMode.LogRequestNameAndContent)
{
content = $":\n{innerMessage.ToJsonString()}";
}
LogManager.Log($"'{innerMessage.GetType().Name}' received on '{handler.Method.DeclaringType.Name}.{handler.Method.Name}'{content}");
}
if (handler.Method.ReturnType == typeof(Task))
{
((Task)handler.Method.Invoke(handler.Handler, new object[]
{
innerMessage,
e.Container.Token,
sender,
})).Wait();
}
else
{
handler.Method.Invoke(handler.Handler, new object[]
{
innerMessage,
e.Container.Token,
sender,
});
}
}
}
catch (Exception ex)
{
LogManager.Log(ex.GetFirstIfAggregate(), $"Error invoking external bridge handler for request '{genericType.Name}'.");
try
{
(sender as ITransporter).SendErrorResponse(ex.GetFirstIfAggregate(), e.Container.Token);
}
catch { }
}
}
}
catch (Exception ex)
{
LogManager.Log(ex, $"An error occurred while trying or invoking an external bridge request handler for '{e.Container.Type}'.");
try
{
(sender as ITransporter).SendErrorResponse(ex.GetFirstIfAggregate(), e.Container.Token);
}
catch { }
}
});
}
}
#endregion
#region Public Methods
/// <summary>
/// Starts this instance.
/// </summary>
public void Start()
{
if (!IsStarted)
{
LogManager.Log("Starting external bridge service...");
LogManager.Log("Starting TCP server...");
try
{
_tcpServer.Start();
}
catch (Exception ex)
{
LogManager.Log(ex, "Error starting external bridge TCP server.");
}
LogManager.Log("Starting discovery service...");
try
{
_discoveryService.Start();
}
catch (Exception ex)
{
LogManager.Log(ex, "Error starting external bridge discovery service.");
}
if (SignalRConfiguration.Enabled)
{
StartSignalR();
}
IsStarted = true;
_enabled = true;
RaisePropertyChanged(nameof(Enabled));
}
}
private void StartSignalR()
{
if (!_enabled || _isSignalRConnected) return;
try
{
try
{
if (_connection != null)
{
_connection.Dispose();
}
}
catch { }
LogManager.Log("Starting SignalR service...");
_connection = new HubConnection(SignalRConfiguration.Address);
_proxy = _connection.CreateHubProxy(SignalRConfiguration.Hub);
_proxy.On<String>("OnSessionCreated", OnSignalRSessionCreated);
_connection.Start();
_connection.StateChanged += (x) =>
{
if (x.NewState == ConnectionState.Connected)
{
try
{
LogManager.Log("External Bridge Service SignalR Connected. Registering machine...");
_proxy.Invoke("RegisterMachine", new MachineInfo()
{
SerialNumber = Machine.SerialNumber,
Organization = Machine.Organization.Name,
});
_isSignalRConnected = true;
if (_signalrPingTimer == null)
{
_signalrPingTimer = new System.Timers.Timer(TimeSpan.FromMinutes(5).TotalMilliseconds);
_signalrPingTimer.Elapsed += _signalrPingTimer_Elapsed;
_signalrPingTimer.Start();
}
}
catch (Exception ex)
{
LogManager.Log(ex, "Error registering machine via SignalR.");
}
}
else if (x.NewState == ConnectionState.Disconnected)
{
_isSignalRConnected = false;
if (Enabled)
{
LogManager.Log("External Bridge Service SignalR Disconnected/Failed. Reconnecting in 1 minute...");
TimeoutTask.StartNew(StartSignalR, TimeSpan.FromMinutes(1));
}
}
else if (x.NewState == ConnectionState.Reconnecting)
{
LogManager.Log("External Bridge Service SignalR Connection Lost. Reconnecting...");
//Will go invoke state change again with "connected" if successful...
}
};
}
catch (Exception ex)
{
LogManager.Log(ex, "Error initializing External Bridge SignalR. Will try again in 5 minutes...");
TimeoutTask.StartNew(StartSignalR, TimeSpan.FromMinutes(5));
}
}
private async void _signalrPingTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (Enabled && SignalRConfiguration.Enabled)
{
if (_proxy != null)
{
try
{
var result = await _proxy.Invoke<String>("Ping");
}
catch
{
Debug.WriteLine("Error pinging to machine service via SignalR.");
}
}
}
}
/// <summary>
/// Stops this instance.
/// </summary>
public async void Stop()
{
if (IsStarted)
{
try
{
_tcpServer.Stop();
_discoveryService.Stop();
}
catch (Exception ex)
{
LogManager.Log(ex, "Error disposing TCP discovery services.");
}
foreach (var receiver in _receivers.ToList())
{
try
{
await receiver.Disconnect();
}
catch (Exception ex)
{
LogManager.Log(ex, $"Error disconnecting receiver {receiver.ComponentName}.");
}
}
_enabled = false;
if (SignalRConfiguration.Enabled)
{
try
{
_isSignalRConnected = false;
await _proxy.Invoke("UnregisterMachine");
_connection.Stop();
_connection.Dispose();
}
catch (Exception ex)
{
LogManager.Log(ex, "Error disposing SignalR connection.");
}
}
IsStarted = false;
HasSessions = false;
RaisePropertyChanged(nameof(Enabled));
}
}
/// <summary>
/// Disconnects the current remote client session.
/// </summary>
public async void DisconnectFullControlSession()
{
var sessionReceiver = _receivers.SingleOrDefault(x => x.IsLoggedIn && x.LoginIntent == ExternalBridgeLoginIntent.FullControl);
if (sessionReceiver != null)
{
await sessionReceiver.Disconnect();
}
}
#endregion
#region Virtual Methods
protected virtual void OnOperatorResponseReceived(MessageContainer container)
{
try
{
switch (container.Type)
{
case MessageType.StartDiagnosticsResponse:
_receivers.ToList().Where(x => x.IsLoggedInAndRequiresDiagnostics && x.RequiresDiagnostics).ToList().ForEach(x => x.UpdateDiagnostics(container));
break;
case MessageType.StartDebugLogResponse:
_receivers.ToList().Where(x => x.IsLoggedInAndRequiresDiagnostics && x.RequiresDebugLogs).ToList().ForEach(x => x.UpdateDebugLogs(container));
break;
case MessageType.StartEventsNotificationResponse:
_receivers.ToList().Where(x => x.IsLoggedInAndRequiresDiagnostics && x.RequiresEventsNotification).ToList().ForEach(x => x.UpdateEvents(container));
break;
case MessageType.StartMachineStatusUpdateResponse:
_receivers.ToList().Where(x => x.IsLoggedInAndRequiresDiagnostics && x.RequiresMachineStatusUpdate).ToList().ForEach(x => x.UpdateMachineStatus(container));
break;
}
}
catch { }
}
#endregion
#region Handlers Registration
public void RegisterRequestHandler(IExternalBridgeRequestHandler handler)
{
foreach (var method in handler.GetType().GetMethods())
{
var att = method.GetCustomAttribute<ExternalBridgeRequestHandlerMethodAttribute>();
if (att != null)
{
_requestHandlers.Add(att.Type is IMessage ? att.Type.Name : att.Type.FullName, new RequestHandler()
{
Handler = handler,
Method = method,
LoggingMode = att.LoggingMode,
});
}
}
}
public void UnregisterRequestHandler(IExternalBridgeRequestHandler handler)
{
foreach (var h in _requestHandlers.Where(x => x.Value.Handler == handler).ToList())
{
_requestHandlers.Remove(h.Key);
}
}
#endregion
}
}