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