diff options
| author | Mirta <mirta@twine-s.com> | 2020-12-30 16:39:52 +0200 |
|---|---|---|
| committer | Mirta <mirta@twine-s.com> | 2020-12-30 16:39:52 +0200 |
| commit | 00a491d93733d4625ad329b2ba8237f445364b3f (patch) | |
| tree | 4b24c6fa78d7648f4bb7cefafa464bb0b063fec4 /Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeService.cs | |
| parent | 124ad4150f80c6846fdee41dbbda9848c105f6e5 (diff) | |
| download | Tango-00a491d9.tar.gz Tango-00a491d9.zip | |
merge
Diffstat (limited to 'Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeService.cs')
| -rw-r--r-- | Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeService.cs | 908 |
1 files changed, 452 insertions, 456 deletions
diff --git a/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeService.cs b/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeService.cs index 4218f9e9a..7ad03f3a7 100644 --- a/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeService.cs +++ b/Software/Visual_Studio/Tango.Integration/ExternalBridge/ExternalBridgeService.cs @@ -21,41 +21,23 @@ 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; -using System.Collections.ObjectModel; namespace Tango.Integration.ExternalBridge { - public class ExternalBridgeService : ExtendedObject, IExternalBridgeService + public class ExternalBridgeService : BasicTransporter, IExternalBridgeService { - private List<ExternalBridgeReceiver> _receivers; private UdpDiscoveryService<ExternalBridgeUdpDiscoveryPacket> _discoveryService; private TcpServer _tcpServer; + private bool _send_app_logs; + private String _app_logs_token; + private Dictionary<MessageType, Action<MessageContainer>> _messageHandlers; 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; } - } + private String _eventsNotificationsToken; + private String _machineStatusUpdateToken; + private String _diagnosticsNotificationToken; + private String _debugLogsNotificationToken; + private bool _resend_diagnostics_and_debug; #region Events @@ -67,7 +49,7 @@ namespace Tango.Integration.ExternalBridge /// <summary> /// Occurs when the last client has been disconnected. /// </summary> - public event EventHandler FullControlSessionDisconnected; + public event EventHandler ClientDisconnected; /// <summary> /// Occurs when the service has received a new color profile request. @@ -78,29 +60,6 @@ namespace Tango.Integration.ExternalBridge #region Properties - public static HashSet<MessageType> SafetyLevelOperations { get; private set; } - - /// <summary> - /// Gets the collection of logged-in receivers. - /// </summary> - public List<ExternalBridgeReceiver> ActiveReceivers - { - get - { - return _receivers.Where(x => x.IsLoggedIn).ToList(); - } - } - - /// <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. @@ -150,60 +109,45 @@ namespace Tango.Integration.ExternalBridge } } - private bool _hasSessions; + private bool _isInSession; /// <summary> - /// Gets a value indicating whether there are any connected sessions. + /// Gets a value indicating whether a remote client is authenticated and in session. /// </summary> - public bool HasSessions + public bool IsInSession { - get { return _hasSessions; } - private set { _hasSessions = value; RaisePropertyChangedAuto(); } + get { return _isInSession; } + private set { _isInSession = value; RaisePropertyChangedAuto(); } } - private ExternalBridgeReceiver _fullControlSessionReceiver; /// <summary> - /// Gets the current full control session receiver. + /// Gets the current session login intent. /// </summary> - public ExternalBridgeReceiver FullControlSessionReceiver - { - get { return _fullControlSessionReceiver; } - private set { _fullControlSessionReceiver = value; RaisePropertyChangedAuto(); } - } + public ExternalBridgeLoginIntent SessionIntent { get; private set; } #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; + _messageHandlers = new Dictionary<MessageType, Action<MessageContainer>>(); + RegisterMessageHandlers(); + _tcpServer = new TcpServer(_external_bridge_port); _tcpServer.ClientConnected += _tcpServer_ClientConnected; LogManager.NewLog += LogManager_NewLog; + + KeepAliveTimeout = TimeSpan.FromSeconds(5); + KeepAliveRetries = 2; } /// <summary> @@ -252,12 +196,12 @@ namespace Tango.Integration.ExternalBridge { if (MachineOperator != null) { + MachineOperator.StateChanged -= MachineOperator_StateChanged; + MachineOperator.StateChanged += MachineOperator_StateChanged; MachineOperator.StatusChanged -= MachineOperator_StatusChanged; MachineOperator.StatusChanged += MachineOperator_StatusChanged; MachineOperator.PendingResponseReceived -= MachineOperator_PendingResponseReceived; MachineOperator.PendingResponseReceived += MachineOperator_PendingResponseReceived; - MachineOperator.EventsNotification -= MachineOperator_EventsNotification; - MachineOperator.EventsNotification += MachineOperator_EventsNotification; } } @@ -272,551 +216,603 @@ namespace Tango.Integration.ExternalBridge /// <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(); - RaisePropertyChanged(nameof(ActiveReceivers)); + if (!IsInSession) + { + UseKeepAlive = false; + LogManager.Log("External bridge client connected from: " + e.Socket.GetIPAddress()); + Adapter = new TcpTransportAdapter(e.Socket); + await Connect(); + } + else + { + e.Socket.Dispose(); + } } - private async void OnSignalRSessionCreated(String sessionID) + /// <summary> + /// Machines the operator state changed. + /// </summary> + /// <param name="sender">The sender.</param> + /// <param name="e">The e.</param> + private void MachineOperator_StateChanged(object sender, TransportComponentState e) { - try + //Do nothing right now. + if (e != TransportComponentState.Connected) { - 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(); - RaisePropertyChanged(nameof(ActiveReceivers)); - } - catch (Exception ex) - { - LogManager.Log(ex, "Error initializing SignalR ExternalBridgeReceiver when session created."); + _resend_diagnostics_and_debug = true; } } - private void MachineOperator_StatusChanged(object sender, MachineStatuses status) + private async void MachineOperator_StatusChanged(object sender, MachineStatuses e) { - try + if (e == MachineStatuses.ReadyToDye && _machineOperator.State == TransportComponentState.Connected) { - _receivers.ToList().Where(x => x.IsLoggedInAndRequiresDiagnostics).ToList().ForEach(x => x.UpdateMachineOperatorStatus((UpdateStatus)status)); + if (IsInSession && _resend_diagnostics_and_debug) + { + _resend_diagnostics_and_debug = false; + + if (_diagnosticsNotificationToken != null) + { + var msg = MessageFactory.CreateTangoMessage<StartDiagnosticsRequest>(_diagnosticsNotificationToken); + _machineOperator.SendContinuousRequest<StartDiagnosticsRequest, StartDiagnosticsResponse>(msg); + } + if (_debugLogsNotificationToken != null) + { + var msg = MessageFactory.CreateTangoMessage<StartDebugLogRequest>(_debugLogsNotificationToken); + _machineOperator.SendContinuousRequest<StartDebugLogRequest, StartDebugLogResponse>(msg); + } + } } - catch (Exception ex) + + if (IsInSession) { - LogManager.Log(ex, "Error updating status of external bridge service client."); + try + { + UpdateStatus s = (UpdateStatus)e; + + await SendRequest<UpdateStatusRequest, UpdateStatusResponse>(new UpdateStatusRequest() + { + Status = s, + }); + } + 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) + if (State == TransportComponentState.Connected && _send_app_logs) { - var msg = MessageFactory.CreateTangoMessage<StartApplicationLogsResponse>(new StartApplicationLogsResponse() + try { - LogItem = ByteString.CopyFrom(e.Serialize()) - }); - - msg.ToBytes(); - - toSend.ToList().Where(x => x.State == TransportComponentState.Connected).ToList().ForEach(x => x.UpdateApplicationLogs(msg.Container)); + SendResponse<StartApplicationLogsResponse>(new StartApplicationLogsResponse() + { + LogItem = ByteString.CopyFrom(e.Serialize()) + }, _app_logs_token); + } + catch { } } } + private void MachineOperator_PendingResponseReceived(object sender, MessageContainer container) { OnOperatorResponseReceived(container); } - private void MachineOperator_EventsNotification(object sender, StartEventsNotificationResponse e) + #endregion + + #region Public Methods + + /// <summary> + /// Starts this instance. + /// </summary> + public void Start() { - try + if (!IsStarted) { - MessageContainer container = new MessageContainer(); - container.Type = MessageType.StartEventsNotificationResponse; - container.Continuous = true; - container.Data = e.ToByteString(); + _tcpServer.Start(); + _discoveryService.Start(); - _receivers.ToList().Where(x => x.IsLoggedInAndRequiresDiagnostics && x.RequiresEventsNotification).ToList().ForEach(x => x.UpdateEvents(container)); - } - catch (Exception ex) - { - LogManager.Log(ex, "Error on external bridge machine events broadcast."); + IsStarted = true; + _enabled = true; + RaisePropertyChanged(nameof(Enabled)); } } - #endregion - - #region Receiver Events Handlers - - private void Receiver_Disconnected(object sender, EventArgs e) + /// <summary> + /// Stops this instance. + /// </summary> + public async void Stop() { - 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) + if (IsStarted) { - FullControlSessionDisconnected?.Invoke(this, new EventArgs()); - } - - HasSessions = _receivers.Count(x => x.IsLoggedIn) > 0; + _tcpServer.Stop(); + _discoveryService.Stop(); - FullControlSessionReceiver = _receivers.SingleOrDefault(x => x.IsLoggedIn && x.LoginIntent == ExternalBridgeLoginIntent.FullControl); + IsStarted = false; + IsInSession = false; + _enabled = false; + RaisePropertyChanged(nameof(Enabled)); - //Notify request handlers about this receiver disconnected. - foreach (var requestHandler in _requestHandlers.ToList().Select(x => x.Value.Handler)) - { try { - requestHandler.OnReceiverDisconnected(receiver); + await Disconnect(); } - catch { } //Ignore exceptions on handler. + catch { } } - - RaisePropertyChanged(nameof(ActiveReceivers)); } - private void Receiver_ColorProfileRequest(object sender, ColorProfileRequestEventArgs e) + /// <summary> + /// Disconnects the current remote client session. + /// </summary> + public async void DisconnectSession() { - ColorProfileRequest?.Invoke(this, e); + await Disconnect(); } - 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; - } - - RaisePropertyChanged(nameof(ActiveReceivers)); - }, (reason) => - { - //Declined - e.Decline(reason); - }); + #endregion - args.Request = request; - args.Address = e.Address; - ConnectionRequest?.Invoke(this, args); - } + #region Override Methods - private void Receiver_ReceiverRequestReceived(object sender, ExternalBridgeReceiverRequestReceivedEventArgs e) + protected override void OnRequestReceived(MessageContainer container) { - ExternalBridgeReceiver receiver = sender as ExternalBridgeReceiver; + base.OnRequestReceived(container); - if (e.Container.Type == MessageType.GenericRequest || _requestHandlers.ContainsKey(e.Container.Type.ToString())) + try { - e.Handled = true; - - ThreadFactory.StartNew(() => + if (Enabled) { - try + if (container.Type == MessageType.ConnectRequest || container.Type == MessageType.DisconnectRequest) { - var message = MessageFactory.ExtractMessageFromContainer(e.Container); - - if (e.Container.Type != MessageType.GenericRequest) //Handle standard PMR messages. + //Do nothing ! + } + else + { + if (IsInSession) { - var handler = _requestHandlers[e.Container.Type.ToString()]; - - if (handler.Method.ReturnType == typeof(Task)) + if (container.Type == MessageType.ExternalBridgeLoginRequest) { - ((Task)handler.Method.Invoke(handler.Handler, new object[] - { - message, - e.Container.Token, - sender, - })).Wait(); + SendErrorResponse(new AuthenticationException("Machine is already in session."), container.Token); + return; } - else - { - handler.Method.Invoke(handler.Handler, new object[] - { - message, - e.Container.Token, - sender, - }); - } - } - else //Handle GenericRequest with inner JSON/BSON formated generic message. - { - var genericType = Type.GetType((message as GenericRequest).Type); - if (genericType != null) + if (_messageHandlers.ContainsKey(container.Type)) { try { - if (_requestHandlers.ContainsKey(genericType.FullName)) + try { - var innerMessage = GenericMessageSerializer.DeserializeFromByteString(genericType, (message as GenericRequest).Data, receiver.GenericProtocol); - - 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, - }); - } + _messageHandlers[container.Type](container); } - else + catch (Exception ex) { - receiver.SendErrorResponse(new NotSupportedException("Request message not supported on this PPC version."), e.Container.Token); + SendErrorResponse(ex, container.Token); } } catch (Exception ex) { - LogManager.Log(ex.GetFirstIfAggregate(), $"Error invoking external bridge handler for request '{genericType.Name}'."); - - try + if (ex is ResponseErrorException) { - receiver.SendErrorResponse(ex.GetFirstIfAggregate(), e.Container.Token); + SendResponse((ex as ResponseErrorException).Container); + } + else + { + SendErrorResponse(ex, container.Token); } - catch { } } } else { - receiver.SendErrorResponse(new NotSupportedException("Request message not supported on this PPC version."), e.Container.Token); + OnAnyRequest(container); } } - } - catch (Exception ex) - { - LogManager.Log(ex, $"An error occurred while trying or invoking an external bridge request handler for '{e.Container.Type}'."); - try + else { - receiver.SendErrorResponse(ex.GetFirstIfAggregate(), e.Container.Token); + if (container.Type == MessageType.ExternalBridgeLoginRequest) + { + OnExternalBridgeLoginRequest(container); + } + else if (container.Type == MessageType.ColorProfileRequest) + { + OnColorProfileRequest(container); + } } - catch { } } - }); + } + } + catch (Exception ex) + { + LogManager.Log(ex, String.Format("An error occurred while processing a request message '{0}' from the remote host.", container.Type)); } } - #endregion + public async override Task Disconnect() + { + _send_app_logs = false; - #region Public Methods + try + { + if (IsInSession) + { + await SendRequest<ExternalBridgeLogoutRequest, ExternalBridgeLogoutResponse>(new ExternalBridgeLogoutRequest(), TimeSpan.FromSeconds(0.5)); + } + } + catch (Exception ex) + { + LogManager.Log(ex, "Error sending an external bridge log out request."); + } + finally + { + ClearQueues(); + } - /// <summary> - /// Starts this instance. - /// </summary> - public void Start() + OnClientDisconnected(); + } + + protected override void OnFailed(Exception ex) { - if (!IsStarted) + if (ex is KeepAliveException) { - LogManager.Log("Starting external bridge service..."); + LogManager.Log("External bridge client has failed to provide a keep alive response. Disconnecting session..."); + } + + base.OnFailed(ex); + } + #endregion + + #region Virtual Methods + + protected async virtual void OnClientDisconnected() + { + _eventsNotificationsToken = null; + _machineStatusUpdateToken = null; + _diagnosticsNotificationToken = null; + _debugLogsNotificationToken = null; + IsInSession = false; + _send_app_logs = false; - LogManager.Log("Starting TCP server..."); + if (MachineOperator.State == TransportComponentState.Connected) + { try { - _tcpServer.Start(); + await MachineOperator.SendRequest(new StopDiagnosticsRequest()); } catch (Exception ex) { - LogManager.Log(ex, "Error starting external bridge TCP server."); + LogManager.Log(ex); } - LogManager.Log("Starting discovery service..."); try { - _discoveryService.Start(); + await MachineOperator.SendRequest(new StopDebugLogRequest()); } catch (Exception ex) { - LogManager.Log(ex, "Error starting external bridge discovery service."); + LogManager.Log(ex); } + } - if (SignalRConfiguration.Enabled) - { - StartSignalR(); - } + ClientDisconnected?.Invoke(this, new EventArgs()); - IsStarted = true; - _enabled = true; - RaisePropertyChanged(nameof(Enabled)); + try + { + await base.Disconnect(); } + catch (Exception ex) + { + LogManager.Log(ex); + } + + LogManager.Log("External bridge client disconnected."); } - private void StartSignalR() + protected virtual void OnOperatorResponseReceived(MessageContainer container) { - 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 (IsInSession) { - if (x.NewState == ConnectionState.Connected) + if (container.Type == MessageType.StartEventsNotificationResponse) { - try + if (_eventsNotificationsToken != null) { - 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(); - } + container.Token = _eventsNotificationsToken; + SendResponse(container); } - catch (Exception ex) + } + if (container.Type == MessageType.StartMachineStatusUpdateResponse) + { + if (_machineStatusUpdateToken != null) { - LogManager.Log(ex, "Error registering machine via SignalR."); + container.Token = _machineStatusUpdateToken; + SendResponse(container); } } - else if (x.NewState == ConnectionState.Disconnected) + else if (container.Type == MessageType.StartDiagnosticsResponse) { - _isSignalRConnected = false; - - if (Enabled) + if (_diagnosticsNotificationToken != null) { - LogManager.Log("External Bridge Service SignalR Disconnected/Failed. Reconnecting in 1 minute..."); - TimeoutTask.StartNew(StartSignalR, TimeSpan.FromMinutes(1)); + container.Token = _diagnosticsNotificationToken; + SendResponse(container); } } - else if (x.NewState == ConnectionState.Reconnecting) + else if (container.Type == MessageType.StartDebugLogResponse) { - LogManager.Log("External Bridge Service SignalR Connection Lost. Reconnecting..."); - //Will go invoke state change again with "connected" if successful... + if (_debugLogsNotificationToken != null) + { + container.Token = _debugLogsNotificationToken; + SendResponse(container); + } } - }; + } } - catch (Exception ex) - { - LogManager.Log(ex, "Error initializing External Bridge SignalR. Will try again in 5 minutes..."); + catch { } + } - TimeoutTask.StartNew(StartSignalR, TimeSpan.FromMinutes(5)); - } + #endregion + + #region Message Handlers + + private void RegisterMessageHandlers() + { + _messageHandlers.Add(MessageType.ExternalBridgeLogoutRequest, OnExternalBridgeLogoutRequest); + + _messageHandlers.Add(MessageType.StartApplicationLogsRequest, OnStartApplicationLogsRequest); + _messageHandlers.Add(MessageType.StopApplicationLogsRequest, OnStopApplicationLogsRequest); + + _messageHandlers.Add(MessageType.JobRequest, OnJobRequest); + + //Events + _messageHandlers.Add(MessageType.StartEventsNotificationRequest, OnStartEventsNotificationRequest); + _messageHandlers.Add(MessageType.StopEventsNotificationRequest, OnStopEventsNotificationRequest); + + //Machine Status + _messageHandlers.Add(MessageType.StartMachineStatusUpdateRequest, OnStartMachineStatusUpdateRequest); + _messageHandlers.Add(MessageType.StopMachineStatusUpdateRequest, OnStopMachineStatusUpdateRequest); + + //Diagnostics + _messageHandlers.Add(MessageType.StartDiagnosticsRequest, OnStartDiagnosticsRequest); + + //Debug Logs + _messageHandlers.Add(MessageType.StartDebugLogRequest, OnStartDebugLogRequest); } - private async void _signalrPingTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) + protected virtual async void OnAnyRequest(MessageContainer container) { - if (Enabled && SignalRConfiguration.Enabled) + if (SessionIntent == ExternalBridgeLoginIntent.ColorProfile) { - if (_proxy != null) - { - try - { - var result = await _proxy.Invoke<String>("Ping"); - } - catch - { - Debug.WriteLine("Error pinging to machine service via SignalR."); - } - } + await SendErrorResponse(new AuthenticationException("The specified intent does not grant the specified action."), container.Token); + return; } - } - /// <summary> - /// Stops this instance. - /// </summary> - public async void Stop() - { - if (IsStarted) + if (!container.Continuous) { try { - _tcpServer.Stop(); - _discoveryService.Stop(); + var response = await MachineOperator.SendRequest(container); + await SendResponse(response); } + //catch (TimeoutException) + //{ + + //} catch (Exception ex) { - LogManager.Log(ex, "Error disposing TCP discovery services."); + await SendErrorResponse(ex, container.Token); } - - foreach (var receiver in _receivers.ToList()) + } + else + { + try { - try + MachineOperator.SendContinuousRequest(container).Subscribe((response) => { - await receiver.Disconnect(); - } - catch (Exception ex) + if (Enabled && IsInSession) + { + SendResponse(response); + } + + }, (ex) => { - LogManager.Log(ex, $"Error disconnecting receiver {receiver.ComponentName}."); - } + if (Enabled) + { + if (ex is ResponseErrorException) + { + SendResponse((ex as ResponseErrorException).Container); + } + } + }); + } + catch (Exception ex) + { + await SendErrorResponse(ex, container.Token); } + } + } - _enabled = false; + protected async virtual void OnExternalBridgeLoginRequest(MessageContainer container) + { + var request = MessageFactory.ParseTangoMessageFromContainer<ExternalBridgeLoginRequest>(container); + + LogManager.Log($"External bridge login attempt:\nIntent: {request.Message.Intent}\nMessage:\n{request.Message.ToJsonString()}"); - if (SignalRConfiguration.Enabled) + if (MachineOperator.Status != MachineStatuses.Printing || request.Message.Intent != ExternalBridgeLoginIntent.FullControl) + { + ExternalBridgeClientConnectedEventArgs args = new ExternalBridgeClientConnectedEventArgs(); + args.Request = request; + args.IpAddress = Adapter.Address; + ConnectionRequest?.Invoke(this, args); + + var response = new ExternalBridgeLoginResponse(); + response.Authenticated = args.Confirmed; + response.SerialNumber = Machine.SerialNumber; + response.DeviceInformation = MachineOperator.DeviceInformation; + + IsInSession = args.Confirmed; + + if (IsInSession) { - try - { - _isSignalRConnected = false; - await _proxy.Invoke("UnregisterMachine"); - _connection.Stop(); - _connection.Dispose(); - } - catch (Exception ex) - { - LogManager.Log(ex, "Error disposing SignalR connection."); - } + LogManager.Log("External bridge client has logged-in successfully."); + UseKeepAlive = true; + MachineOperator.EnableDiagnostics = false; + MachineOperator.EnableEmbeddedDebugging = false; + SessionIntent = request.Message.Intent; + } + else + { + LogManager.Log("External bridge client login failed, invalid password."); } - IsStarted = false; - HasSessions = false; - RaisePropertyChanged(nameof(Enabled)); + await SendResponse<ExternalBridgeLoginResponse>(response, container.Token, null, null, IsInSession ? null : "Invalid password or intent."); + } + else + { + LogManager.Log($"External bridge client login failed because the machine is currently printing and '{ExternalBridgeLoginIntent.FullControl}' intent was requested."); + await SendResponse<ExternalBridgeLoginResponse>(new ExternalBridgeLoginResponse(), container.Token, false, ErrorCode.GeneralError, $"Machine connection with '{ExternalBridgeLoginIntent.FullControl}' intent is not permitted while printing."); } } - /// <summary> - /// Disconnects the current remote client session. - /// </summary> - public async void DisconnectFullControlSession() + protected async virtual void OnExternalBridgeLogoutRequest(MessageContainer container) { - var sessionReceiver = _receivers.SingleOrDefault(x => x.IsLoggedIn && x.LoginIntent == ExternalBridgeLoginIntent.FullControl); - if (sessionReceiver != null) + try + { + await SendResponse<ExternalBridgeLogoutResponse>(new ExternalBridgeLogoutResponse(), container.Token); + } + catch (Exception ex) { - await sessionReceiver.Disconnect(); + LogManager.Log(ex); } + finally + { + ClearQueues(); + } + + OnClientDisconnected(); } - #endregion + protected virtual void OnStartApplicationLogsRequest(MessageContainer container) + { + if (SessionIntent == ExternalBridgeLoginIntent.Diagnostics || SessionIntent == ExternalBridgeLoginIntent.FullControl) + { + _app_logs_token = container.Token; + _send_app_logs = true; + SendResponse<StartApplicationLogsResponse>(new StartApplicationLogsResponse(), container.Token); + } + else + { + throw new AuthenticationException("The specified intent does not grant the specified action."); + } + } - #region Virtual Methods + protected virtual void OnStopApplicationLogsRequest(MessageContainer container) + { + if (SessionIntent == ExternalBridgeLoginIntent.Diagnostics || SessionIntent == ExternalBridgeLoginIntent.FullControl) + { + _send_app_logs = false; + SendResponse<StopApplicationLogsResponse>(new StopApplicationLogsResponse() { }, container.Token); + SendResponse<StartApplicationLogsResponse>(new StartApplicationLogsResponse() { }, _app_logs_token, true); + } + else + { + throw new AuthenticationException("The specified intent does not grant the specified action."); + } + } - protected virtual void OnOperatorResponseReceived(MessageContainer container) + protected virtual void OnJobRequest(MessageContainer container) { - try + if (SessionIntent != ExternalBridgeLoginIntent.FullControl) { - 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; - case MessageType.StartInkFillingStatusResponse: - _receivers.ToList().Where(x => x.IsLoggedInAndRequiresDiagnostics && x.RequiresInkFillingStatus).ToList().ForEach(x => x.UpdateInkFillingStatus(container)); - break; - } + throw new InvalidOperationException($"Job execution is disabled while session intent is '{SessionIntent}'."); + } + if (MachineOperator.Status != MachineStatuses.ReadyToDye) + { + throw new InvalidOperationException($"Could not execute job while machine operator status is '{MachineOperator.Status}'."); + } + else + { + OnAnyRequest(container); } - catch { } } - #endregion + protected virtual void OnStartEventsNotificationRequest(MessageContainer container) + { + _eventsNotificationsToken = container.Token; + SendResponse<StartEventsNotificationResponse>(new StartEventsNotificationResponse(), container.Token); + } + + protected virtual void OnStopEventsNotificationRequest(MessageContainer container) + { + if (_eventsNotificationsToken != null) + { + SendResponse<StartEventsNotificationResponse>(new StartEventsNotificationResponse(), _eventsNotificationsToken, true); + _eventsNotificationsToken = null; + SendResponse<StopEventsNotificationResponse>(new StopEventsNotificationResponse(), container.Token); + } + } - #region Handlers Registration + protected virtual void OnStartMachineStatusUpdateRequest(MessageContainer container) + { + _machineStatusUpdateToken = container.Token; + SendResponse<StartMachineStatusUpdateResponse>(new StartMachineStatusUpdateResponse(), container.Token); + } - public void RegisterRequestHandler(IExternalBridgeRequestHandler handler) + protected virtual void OnStopMachineStatusUpdateRequest(MessageContainer container) { - foreach (var method in handler.GetType().GetMethods()) + if (_machineStatusUpdateToken != null) { - 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, - }); - } + SendResponse<StartMachineStatusUpdateResponse>(new StartMachineStatusUpdateResponse(), _machineStatusUpdateToken, true); + _machineStatusUpdateToken = null; + SendResponse<StopMachineStatusUpdateResponse>(new StopMachineStatusUpdateResponse(), container.Token); + } + } + + protected virtual void OnStartDiagnosticsRequest(MessageContainer container) + { + if (_diagnosticsNotificationToken == null) + { + _diagnosticsNotificationToken = container.Token; + _machineOperator.SendContinuousRequest(container); } } - public void UnregisterRequestHandler(IExternalBridgeRequestHandler handler) + protected virtual void OnStartDebugLogRequest(MessageContainer container) { - foreach (var h in _requestHandlers.Where(x => x.Value.Handler == handler).ToList()) + if (_debugLogsNotificationToken == null) { - _requestHandlers.Remove(h.Key); + _debugLogsNotificationToken = container.Token; + _machineOperator.SendContinuousRequest(container); } } + private void OnColorProfileRequest(MessageContainer container) + { + var request = MessageFactory.ParseTangoMessageFromContainer<ColorProfileRequest>(container); + + ColorProfileRequestEventArgs e = new ColorProfileRequestEventArgs(request, async () => + { + //Approved. + await SendResponse<ColorProfileResponse>(new ColorProfileResponse() + { + Approved = true + }, container.Token); + await Task.Delay(500); + await base.Disconnect(); + }, + async () => + { + //Declined. + await SendResponse<ColorProfileResponse>(new ColorProfileResponse(), container.Token); + await Task.Delay(500); + await base.Disconnect(); + }); + + ColorProfileRequest?.Invoke(this, e); + } + #endregion } } |
