Last-Modified: Thu, 30 Jul 2026 19:01:36 GMT
Expires: Sun, 27 Jul 2036 19:01:36 GMT
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;
namespace Tango.Integration.ExternalBridge
{
public class ExternalBridgeService : BasicTransporter, IExternalBridgeService
{
private UdpDiscoveryService<ExternalBridgeUdpDiscoveryPacket> _discoverySevice;
private TcpServer _tcpServer;
private bool _send_app_logs;
private String _app_logs_token;
private Dictionary<MessageType, Action<MessageContainer>> _messageHandlers;
#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 ClientDisconnected;
#endregion
#region Properties
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 _isInSession;
/// <summary>
/// Gets a value indicating whether a remote client is authenticated and in session.
/// </summary>
public bool IsInSession
{
get { return _isInSession; }
private set { _isInSession = value; RaisePropertyChangedAuto(); }
}
/// <summary>
/// Gets the current session login intent.
/// </summary>
public ExternalBridgeLoginIntent SessionIntent { get; private set; }
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="ExternalBridgeService"/> class.
/// </summary>
public ExternalBridgeService()
{
_messageHandlers = new Dictionary<MessageType, Action<MessageContainer>>();
RegisterMessageHandlers();
_tcpServer = new TcpServer(1984);
_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 (_discoverySevice != null && _discoverySevice.IsStarted)
{
_discoverySevice.Stop();
}
_discoverySevice = new UdpDiscoveryService<ExternalBridgeUdpDiscoveryPacket>(8888, new ExternalBridgeUdpDiscoveryPacket()
{
SerialNumber = Machine.SerialNumber,
});
_discoverySevice.BeforeBroadcasting -= _discoverySevice_BeforeBroadcasting;
_discoverySevice.BeforeBroadcasting += _discoverySevice_BeforeBroadcasting;
}
private void _discoverySevice_BeforeBroadcasting(object sender, ExternalBridgeUdpDiscoveryPacket e)
{
e.Time = DateTime.Now.ToString();
}
/// <summary>
/// Called when the machine operator has been changed
/// </summary>
protected virtual void OnMachineOperatorChanged()
{
if (MachineOperator != null)
{
MachineOperator.StateChanged -= MachineOperator_StateChanged;
MachineOperator.StateChanged += MachineOperator_StateChanged;
}
}
#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 client connected from: " + e.Socket.GetIPAddress());
Adapter = new TcpTransportAdapter(e.Socket);
await Connect();
}
/// <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)
{
//Do nothing right now.
}
private void LogManager_NewLog(object sender, Tango.Logging.LogItemBase e)
{
if (State == TransportComponentState.Connected && _send_app_logs)
{
try
{
SendResponse<StartApplicationLogsResponse>(new StartApplicationLogsResponse()
{
LogItem = ByteString.CopyFrom(e.Serialize())
}, _app_logs_token);
}
catch { }
}
}
#endregion
#region Public Methods
/// <summary>
/// Starts this instance.
/// </summary>
public void Start()
{
if (!IsStarted)
{
_tcpServer.Start();
_discoverySevice.Start();
IsStarted = true;
_enabled = true;
RaisePropertyChanged(nameof(Enabled));
}
}
/// <summary>
/// Stops this instance.
/// </summary>
public async void Stop()
{
if (IsStarted)
{
_tcpServer.Stop();
_discoverySevice.Stop();
IsStarted = false;
IsInSession = false;
_enabled = false;
RaisePropertyChanged(nameof(Enabled));
try
{
await Disconnect();
}
catch { }
}
}
/// <summary>
/// Disconnects the current remote client session.
/// </summary>
public async void DisconnectSession()
{
await Disconnect();
}
#endregion
#region Override Methods
protected override void OnRequestReceived(MessageContainer container)
{
base.OnRequestReceived(container);
try
{
if (Enabled)
{
if (container.Type == MessageType.ConnectRequest || container.Type == MessageType.DisconnectRequest)
{
//Do nothing !
}
else
{
if (IsInSession)
{
if (container.Type == MessageType.ExternalBridgeLoginRequest)
{
SendErrorResponse(new AuthenticationException("Machine is already in session."), container.Token);
return;
}
if (_messageHandlers.ContainsKey(container.Type))
{
try
{
try
{
_messageHandlers[container.Type](container);
}
catch (Exception ex)
{
SendErrorResponse(ex, container.Token);
}
}
catch (Exception ex)
{
if (ex is ResponseErrorException)
{
SendResponse((ex as ResponseErrorException).Container);
}
else
{
SendErrorResponse(ex, container.Token);
}
}
}
else
{
OnAnyRequest(container);
}
}
else
{
if (container.Type == MessageType.ExternalBridgeLoginRequest)
{
OnExternalBridgeLoginRequest(container);
}
}
}
}
}
catch (Exception ex)
{
LogManager.Log(ex, String.Format("An error occurred while processing a request message '{0}' from the remote host.", container.Type));
}
}
public async override Task Disconnect()
{
_send_app_logs = false;
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();
}
OnClientDisconnected();
}
#endregion
#region Virtual Methods
protected async virtual void OnClientDisconnected()
{
IsInSession = false;
_send_app_logs = false;
if (MachineOperator.State == TransportComponentState.Connected)
{
try
{
await MachineOperator.SendRequest(new StopDiagnosticsRequest());
}
catch (Exception ex)
{
LogManager.Log(ex);
}
try
{
await MachineOperator.SendRequest(new StopDebugLogRequest());
}
catch (Exception ex)
{
LogManager.Log(ex);
}
}
ClientDisconnected?.Invoke(this, new EventArgs());
try
{
await base.Disconnect();
}
catch (Exception ex)
{
LogManager.Log(ex);
}
LogManager.Log("External bridge client disconnected.");
}
#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);
}
protected virtual async void OnAnyRequest(MessageContainer container)
{
if (SessionIntent == ExternalBridgeLoginIntent.ColorProfile)
{
await SendErrorResponse(new AuthenticationException("The specified intent does not grant the specified action."), container.Token);
return;
}
if (!container.Continuous)
{
try
{
var response = await MachineOperator.SendRequest(container);
await SendResponse(response);
}
//catch (TimeoutException)
//{
//}
catch (Exception ex)
{
await SendErrorResponse(ex, container.Token);
}
}
else
{
try
{
MachineOperator.SendContinuousRequest(container).Subscribe((response) =>
{
if (Enabled && IsInSession)
{
SendResponse(response);
}
}, (ex) =>
{
if (Enabled)
{
if (ex is ResponseErrorException)
{
SendResponse((ex as ResponseErrorException).Container);
}
}
});
}
catch (Exception ex)
{
await SendErrorResponse(ex, container.Token);
}
}
}
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 (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)
{
LogManager.Log("External bridge client has logged-in successfully.");
MachineOperator.EnableDiagnostics = false;
MachineOperator.EnableEmbeddedDebugging = false;
SessionIntent = request.Message.Intent;
}
else
{
LogManager.Log("External bridge client login failed, invalid password.");
}
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.");
}
}
protected async virtual void OnExternalBridgeLogoutRequest(MessageContainer container)
{
try
{
await SendResponse<ExternalBridgeLogoutResponse>(new ExternalBridgeLogoutResponse(), container.Token);
}
catch (Exception ex)
{
LogManager.Log(ex);
}
finally
{
ClearQueues();
}
OnClientDisconnected();
}
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.");
}
}
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 OnJobRequest(MessageContainer container)
{
if (SessionIntent != ExternalBridgeLoginIntent.FullControl)
{
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);
}
}
#endregion
}
}