using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tango.PMR;
using Tango.PMR.Diagnostics;
using Tango.Transport;
using Tango.Transport.Transporters;
using System.Reactive.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Threading;
using Tango.PMR.Common;
using Tango.PMR.Printing;
using System.Reactive.Subjects;
using Tango.PMR.Debugging;
using Tango.Logging;
using Tango.Settings;
using System.IO;
using Tango.BL.Entities;
using Tango.PMR.Hardware;
using Google.Protobuf;
using Tango.PMR.Connection;
namespace Tango.Integration.Operation
{
///
/// Represents the Tango machine operator default implementation.
///
///
///
public class MachineOperator : BasicTransporter, IMachineOperator
{
private bool _diagnosticsSent;
private bool _debugSent;
public static String EmbeddedLogsFolder { get; private set; }
public static String EmbeddedLogsTag { get; private set; }
static MachineOperator()
{
if (EmbeddedLogManager == null)
{
EmbeddedLogManager = new LogManager();
EmbeddedLogsTag = "Embedded";
EmbeddedLogsFolder = Path.Combine(Path.GetDirectoryName(SettingsManager.Default.Folder), "Logs", Path.GetFileNameWithoutExtension(AppDomain.CurrentDomain.FriendlyName), "Embedded");
Directory.CreateDirectory(EmbeddedLogsFolder);
FileLogger fileLogger = new FileLogger(EmbeddedLogsFolder, EmbeddedLogsTag) { Enabled = true };
EmbeddedLogManager.RegisterLogger(fileLogger);
}
}
public MachineOperator() : base()
{
DeviceInformation = new DeviceInformation();
MachineEventsStateProvider = new DefaultMachineEventsStateProvider();
EnableEmbeddedDebugging = true;
LogEmbeddedDebuggingToFile = true;
}
#region Events
///
/// Occurs when there is new diagnostics data available.
///
public event EventHandler DiagnosticsDataAvailable;
///
/// Occurs when a new debug log is available.
///
public event EventHandler DebugLogAvailable;
///
/// Occurs when a request has been sent.
///
public event EventHandler RequestSent;
///
/// Occurs when a response has been sent.
///
public event EventHandler ResponseSent;
///
/// Occurs when a request has timed out.
///
public event EventHandler RequestFailed;
///
/// Occurs when a request response has been received.
///
public event EventHandler ResponseReceived;
#endregion
#region Properties
///
/// Gets the embedded device log manager.
///
public static LogManager EmbeddedLogManager { get; private set; }
private bool _enableDiagnostics;
///
/// Gets or sets a value indicating whether direct the embedded device to send diagnostics messages.
///
public bool EnableDiagnostics
{
get { return _enableDiagnostics; }
set
{
if (_enableDiagnostics != value)
{
_enableDiagnostics = value;
RaisePropertyChangedAuto();
OnEnableDiagnosticsChanged(value);
}
}
}
private bool _enableEmbeddedDebugging;
///
/// Gets or sets a value indicating whether to allow incoming debugging messages.
///
///
///
public bool EnableEmbeddedDebugging
{
get
{
return _enableEmbeddedDebugging;
}
set
{
if (_enableEmbeddedDebugging != value)
{
_enableEmbeddedDebugging = value;
RaisePropertyChangedAuto();
OnEnableEmbeddedDebuggingChanged(value);
}
}
}
private bool _logEmbeddedDebuggingToFile;
///
/// Gets or sets a value indicating whether to automatically save incoming log data from the embedded device.
///
public bool LogEmbeddedDebuggingToFile
{
get { return _logEmbeddedDebuggingToFile; }
set
{
_logEmbeddedDebuggingToFile = value; RaisePropertyChangedAuto();
}
}
///
/// Gets or sets the machine events state provider used to get notifications about current machine events and errors.
///
public IMachineEventsStateProvider MachineEventsStateProvider { get; set; }
///
/// Gets the last process parameters table sent to the embedded device.
///
public ProcessParametersTable CurrentProcessParameters { get; private set; }
///
/// Gets the last hardware configuration sent to the embedded device.
///
public HardwareConfiguration CurrentHardwareConfiguration { get; private set; }
private DeviceInformation _deviceInformation;
///
/// Gets or sets the embedded device information.
///
public DeviceInformation DeviceInformation
{
get { return _deviceInformation; }
set { _deviceInformation = value; RaisePropertyChangedAuto(); }
}
#endregion
#region Virtual Methods
///
/// Called when the enable diagnostics property has been changed
///
/// if set to true [value].
protected virtual async void OnEnableDiagnosticsChanged(bool value)
{
if (value && State == TransportComponentState.Connected && !_diagnosticsSent)
{
var request = new StartDiagnosticsRequest();
bool responseLogged = false;
_diagnosticsSent = true;
SendContinuousRequest(request).ObserveOn(new NewThreadScheduler()).Subscribe(
(response) =>
{
OnDiagnosticsDataAvailable(response);
if (!responseLogged)
{
LogResponseReceived(response.Message);
responseLogged = true;
}
},
(ex) =>
{
_diagnosticsSent = false;
LogRequestFailed(request, ex);
},
() =>
{
_diagnosticsSent = false;
LogManager.Log("Diagnostics response completed!?", LogCategory.Warning);
});
LogRequestSent(request);
}
else
{
_diagnosticsSent = false;
if (State == TransportComponentState.Connected)
{
var req = new StopDiagnosticsRequest();
try
{
LogRequestSent(req);
var res = await SendRequest(req);
LogResponseReceived(res.Message);
}
catch (Exception ex)
{
LogRequestFailed(req, ex);
}
}
}
}
///
/// Called when the enable embedded debugging has been changed
///
/// if set to true [value].
protected async void OnEnableEmbeddedDebuggingChanged(bool value)
{
if (value && State == TransportComponentState.Connected && !_debugSent)
{
var request = new StartDebugLogRequest();
bool responseLogged = false;
_debugSent = true;
SendContinuousRequest(request).ObserveOn(new NewThreadScheduler())
.Subscribe
(
(response) =>
{
if (!responseLogged)
{
LogResponseReceived(response.Message);
responseLogged = true;
}
OnDebugLogAvailable(response);
},
(ex) =>
{
_debugSent = false;
LogRequestFailed(request, ex);
},
() =>
{
_debugSent = false;
});
LogRequestSent(request);
}
else
{
_debugSent = false;
if (State == TransportComponentState.Connected)
{
var req = new StopDebugLogRequest();
try
{
LogRequestSent(req);
var res = await SendRequest(req);
LogResponseReceived(res.Message);
}
catch (Exception ex)
{
LogRequestFailed(req, ex);
}
}
}
}
///
/// Invokes the event.
///
/// The sensors data.
protected virtual void OnDiagnosticsDataAvailable(StartDiagnosticsResponse data)
{
if (MachineEventsStateProvider != null)
{
MachineEventsStateProvider.ApplyEvents(data.Events);
}
DiagnosticsDataAvailable?.Invoke(this, data);
}
///
/// Invokes the event.
///
/// The sensors data.
protected virtual void OnDebugLogAvailable(StartDebugLogResponse data)
{
if (LogEmbeddedDebuggingToFile && EmbeddedLogManager != null)
{
EmbeddedLogManager.Log(new EmbeddedLogItem(data));
}
DebugLogAvailable?.Invoke(this, data);
}
///
/// Called when the request has been sent
///
/// The request.
protected virtual void OnRequestSent(IMessage request)
{
RequestSent?.Invoke(this, request);
}
///
/// Called when the response has been received
///
/// The response.
protected virtual void OnResponseReceived(IMessage response)
{
ResponseReceived?.Invoke(this, response);
}
///
/// Called when the response has been sent
///
/// The response.
protected virtual void OnResponseSent(IMessage response)
{
ResponseSent?.Invoke(this, response);
}
///
/// Called when the request has been failed
///
/// The request.
protected virtual void OnRequestFailed(IMessage request, Exception exception)
{
RequestFailed?.Invoke(this, new RequestFailedEventArgs(request, exception));
}
#endregion
#region Override Methods
///
/// Called when the component state has changed.
///
/// The state.
protected override void OnStateChanged(TransportComponentState state)
{
base.OnStateChanged(state);
if (state != TransportComponentState.Connected)
{
_diagnosticsSent = false;
_debugSent = false;
}
}
///
/// Disconnects the machine operator and the underlying transporter.
///
///
public async override Task Disconnect()
{
if (State == TransportComponentState.Connected)
{
DisconnectRequest request = new DisconnectRequest();
LogRequestSent(request);
try
{
var response = await SendRequest(request);
LogResponseReceived(response.Message);
}
catch (Exception ex)
{
LogRequestFailed(request, ex);
}
}
await base.Disconnect();
}
public async override Task Connect()
{
await base.Connect();
if (State == TransportComponentState.Connected)
{
ConnectRequest request = new ConnectRequest() { Password = "1234" };
LogRequestSent(request);
try
{
var response = await SendRequest(request);
LogResponseReceived(response.Message);
DeviceInformation = response.Message.DeviceInformation;
OnEnableDiagnosticsChanged(EnableDiagnostics);
OnEnableEmbeddedDebuggingChanged(EnableEmbeddedDebugging);
}
catch (Exception ex)
{
LogRequestFailed(request, ex);
await base.Disconnect();
throw ex;
}
}
}
#endregion
#region Public Methods
///
/// Prints the specified job using the specified job parameters.
///
/// The job.
/// Process parameters table
///
public JobHandler Print(Job job, ProcessParametersTable processParameters)
{
CurrentProcessParameters = processParameters;
JobRequest request = new JobRequest();
JobTicket ticket = new JobTicket();
ticket.EnableInterSegment = job.EnableInterSegment;
ticket.InterSegmentLength = job.InterSegmentLength;
ticket.Length = job.Length;
ticket.WindingMethod = (JobWindingMethod)job.WindingMethod.Code;
ticket.Spool = new JobSpool();
job.SpoolType.MapPrimitivesTo(ticket.Spool);
ticket.Spool.JobSpoolType = (JobSpoolType)job.SpoolType.Code;
ProcessParameters process = new ProcessParameters();
processParameters.MapPrimitivesTo(process);
ticket.ProcessParameters = process;
foreach (var segment in job.Segments)
{
JobSegment jobSegment = new JobSegment();
jobSegment.Length = segment.Length;
jobSegment.Name = segment.Name;
foreach (var stop in segment.BrushStops)
{
JobBrushStop jobStop = new JobBrushStop();
jobStop.Index = stop.Index;
jobStop.OffsetPercent = stop.OffsetPercent;
jobStop.OffsetMeters = stop.OffsetMeters;
if (stop.LiquidVolumes == null)
{
stop.SetLiquidVolumes(job.Machine.Configuration, job.Rml, processParameters);
}
foreach (var liquidVolume in stop.LiquidVolumes)
{
JobDispenser dispenser = new JobDispenser();
dispenser.Index = liquidVolume.IdsPack.PackIndex;
dispenser.Volume = liquidVolume.Volume;
dispenser.DispenserLiquidType = (DispenserLiquidType)liquidVolume.IdsPack.LiquidType.Code;
dispenser.DispenserStepDivision = (DispenserStepDivision)liquidVolume.DispenserStepDivision;
dispenser.NanoliterPerPulse = liquidVolume.IdsPack.DispenserType.NlPerPulse;
dispenser.LiquidMaxNanoliterPerCentimeter = liquidVolume.LiquidMaxNanoliterPerCentimeter;
dispenser.NanoliterPerCentimeter = liquidVolume.NanoliterPerCentimeter;
dispenser.NanolitterPerSecond = liquidVolume.NanoliterPerSecond;
dispenser.PulsePerSecond = liquidVolume.PulsePerSecond;
jobStop.Dispensers.Add(dispenser);
}
jobSegment.BrushStops.Add(jobStop);
}
ticket.Segments.Add(jobSegment);
}
request.JobTicket = ticket;
JobHandler handler = null;
handler = new JobHandler(async () =>
{
try
{
var result = await SendRequest(new AbortJobRequest());
handler.RaiseCanceled();
}
catch (Exception ex)
{
LogManager.Log(ex, "Failed to cancel job.");
}
});
LogRequestSent(request);
bool responseLogged = false;
SendContinuousRequest(request).Subscribe((response) =>
{
handler.RaiseStatusReceived(response.Message.Status);
if (!responseLogged)
{
responseLogged = true;
LogResponseReceived(response.Message);
}
}, (ex) =>
{
if (!handler.IsCanceled)
{
handler.RaiseFailed(ex);
LogRequestFailed(request, ex);
}
}, () =>
{
handler.RaiseCompleted();
});
return handler;
}
///
/// Uploads the specified process parameters to the embedded device.
///
/// The process parameters.
///
public async Task UploadProcessParameters(ProcessParametersTable processParameters)
{
UploadProcessParametersRequest request = new UploadProcessParametersRequest();
request.ProcessParameters = new ProcessParameters();
processParameters.MapPrimitivesTo(request.ProcessParameters);
UploadProcessParametersResponse response = null;
try
{
CurrentProcessParameters = processParameters;
LogRequestSent(request);
response = await SendRequest(request);
LogResponseReceived(response);
}
catch (Exception ex)
{
LogRequestFailed(request, ex);
throw ex;
}
return response;
}
///
/// Uploads the specified hardware configuration to the embedded device.
///
/// Machine version.
/// Machine configuration.
///
public async Task UploadHardwareConfiguration(HardwareVersion hardwareVersion, Configuration configuration)
{
HardwareConfiguration hardwareConfiguration = new HardwareConfiguration();
foreach (var dancer in hardwareVersion.HardwareDancers)
{
PMR.Hardware.HardwareDancer item = new PMR.Hardware.HardwareDancer();
dancer.MapPrimitivesTo(item);
item.HardwareDancerType = (PMR.Hardware.HardwareDancerType)dancer.HardwareDancerType.Code;
hardwareConfiguration.Dancers.Add(item);
}
foreach (var motor in hardwareVersion.HardwareMotors)
{
PMR.Hardware.HardwareMotor item = new PMR.Hardware.HardwareMotor();
motor.MapPrimitivesTo(item);
item.HardwareMotorType = (PMR.Hardware.HardwareMotorType)motor.HardwareMotorType.Code;
hardwareConfiguration.Motors.Add(item);
}
foreach (var pid in hardwareVersion.HardwarePidControls)
{
PMR.Hardware.HardwarePidControl item = new PMR.Hardware.HardwarePidControl();
pid.MapPrimitivesTo(item);
item.HardwarePidControlType = (PMR.Hardware.HardwarePidControlType)pid.HardwarePidControlType.Code;
hardwareConfiguration.PidControls.Add(item);
}
foreach (var winder in hardwareVersion.HardwareWinders)
{
PMR.Hardware.HardwareWinder item = new PMR.Hardware.HardwareWinder();
winder.MapPrimitivesTo(item);
item.HardwareWinderType = (PMR.Hardware.HardwareWinderType)winder.HardwareWinderType.Code;
hardwareConfiguration.Winders.Add(item);
}
foreach (var sensor in hardwareVersion.HardwareSpeedSensors)
{
PMR.Hardware.HardwareSpeedSensor item = new PMR.Hardware.HardwareSpeedSensor();
sensor.MapPrimitivesTo(item);
item.HardwareSpeedSensorType = (PMR.Hardware.HardwareSpeedSensorType)sensor.HardwareSpeedSensorType.Code;
hardwareConfiguration.SpeedSensors.Add(item);
}
foreach (var idsPack in configuration.NoneEmptyIdsPacks.OrderBy(x => x.PackIndex))
{
PMR.Hardware.HardwareDispenser item = new PMR.Hardware.HardwareDispenser();
idsPack.DispenserType.MapPrimitivesTo(item);
item.HardwareDispenserType = (PMR.Hardware.HardwareDispenserType)idsPack.DispenserType.Code;
item.Index = idsPack.PackIndex;
hardwareConfiguration.Dispensers.Add(item);
}
UploadHardwareConfigurationRequest request = new UploadHardwareConfigurationRequest();
request.HardwareConfiguration = hardwareConfiguration;
UploadHardwareConfigurationResponse response = null;
try
{
CurrentHardwareConfiguration = hardwareConfiguration;
LogRequestSent(request);
response = await SendRequest(request);
LogResponseReceived(response);
}
catch (Exception ex)
{
LogRequestFailed(request, ex);
throw ex;
}
return response;
}
///
/// Starts jogging the specified motor.
///
/// The request.
///
public async Task StartMotorJogging(MotorJoggingRequest request)
{
MotorJoggingResponse response = null;
try
{
LogRequestSent(request);
response = await SendRequest(request);
LogResponseReceived(response);
}
catch (Exception ex)
{
LogRequestFailed(request, ex);
throw ex;
}
return response;
}
///
/// Stops jogging the specified motor.
///
/// The request.
///
public async Task StopMotorJogging(MotorAbortJoggingRequest request)
{
LogRequestSent(request);
return await SendRequest(request);
}
///
/// Starts homing the specified motor.
///
/// The request.
///
public IObservable StartMotorHoming(MotorHomingRequest request)
{
LogRequestSent(request);
return SendContinuousRequest(request).Select(x => x.Message);
}
///
/// Stops homing the specified motor.
///
/// The request.
///
public async Task StopMotorHoming(MotorAbortHomingRequest request)
{
LogRequestSent(request);
return await SendRequest(request);
}
///
/// Starts jogging the specified dispenser.
///
/// The request.
///
public async Task StartDispenserJogging(DispenserJoggingRequest request)
{
LogRequestSent(request);
return await SendRequest(request);
}
///
/// Stops jogging the specified dispenser.
///
/// The request.
///
public async Task StopDispenserJogging(DispenserAbortJoggingRequest request)
{
LogRequestSent(request);
return await SendRequest(request);
}
///
/// Starts homing the specified dispenser.
///
/// The request.
///
public IObservable StartDispenserHoming(DispenserHomingRequest request)
{
LogRequestSent(request);
return SendContinuousRequest(request).Select(x => x.Message);
}
///
/// Stops homing the specified dispenser.
///
/// The request.
///
public async Task StopDispenserHoming(DispenserAbortHomingRequest request)
{
LogRequestSent(request);
return await SendRequest(request);
}
///
/// Turn on/off the specified digital output pin.
///
/// The request.
///
public async Task SetDigitalOut(SetDigitalOutRequest request)
{
LogRequestSent(request);
return await SendRequest(request);
}
///
/// Starts jogging the thread motion system.
///
/// The request.
///
public async Task StartThreadJogging(ThreadJoggingRequest request)
{
LogRequestSent(request);
return await SendRequest(request);
}
///
/// Stops jogging the thread motion system.
///
/// The request.
///
public async Task StopThreadJogging(ThreadAbortJoggingRequest request)
{
LogRequestSent(request);
return await SendRequest(request);
}
///
/// Sets the specified component value.
///
/// The request.
///
public async Task SetComponentValue(SetComponentValueRequest request)
{
LogRequestSent(request);
return await SendRequest(request);
}
///
/// Resolves the specified event type.
///
/// Type of the event.
///
public async Task ResolveEvent(PMR.Diagnostics.EventType eventType)
{
ResolveEventRequest request = new ResolveEventRequest() { Type = eventType };
LogRequestSent(request);
return await SendRequest(request);
}
#endregion
#region Private Methods
///
/// Logs the request sent.
///
/// The message.
private void LogRequestSent(IMessage message)
{
LogManager.Log(String.Format("Sending request '{0}'...{1}{2}", message.GetType().Name, Environment.NewLine, message.ToJsonString()));
OnRequestSent(message);
}
///
/// Logs the request failed.
///
/// The message.
private void LogRequestFailed(IMessage message, Exception ex)
{
LogManager.Log(String.Format("Request failed '{0}'...{1}{2}{1}{3}", message.GetType().Name, Environment.NewLine, message.ToJsonString(), ex.ToString()), LogCategory.Error);
OnRequestFailed(message, ex);
}
///
/// Logs the response received.
///
/// The message.
private void LogResponseReceived(IMessage message)
{
LogManager.Log(String.Format("Response received '{0}'...{1}{2}", message.GetType().Name, Environment.NewLine, message.ToJsonString()));
OnResponseReceived(message);
}
#endregion
}
}