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;
using Tango.BL.Enumerations;
using Tango.PMR.Stubs;
using System.Threading;
using Tango.Integration.Storage;
using Ionic.Zip;
using Tango.Core.Threading;
using Tango.PMR.IO;
using Tango.Integration.Upgrade;
using Tango.PMR.FirmwareUpgrade;
using Tango.Integration.Logging;
using Tango.Integration.JobRuns;
using Tango.FirmwareUpdateLib.WPF;
using Tango.FirmwareUpdateLib;
using Tango.Core.ExtensionMethods;
using Tango.ColorConversion;
using Tango.Integration.Emergency;
using Tango.PMR.MachineStatus;
using Newtonsoft.Json;
using Tango.PMR.Integration;
namespace Tango.Integration.Operation
{
///
/// Represents the Tango machine operator default implementation.
///
///
///
public class MachineOperator : BasicTransporter, IMachineOperator
{
public const String FIRMWARE_UPGRADE_FOLDER_NAME = "UpgradePackage";
public const String FIRMWARE_UPGRADE_CONFIG_FILE_NAME = "package.cfg";
public const String JOB_DESCRIPTION_FILE_NAME = "job_segments.jdf";
public const int MAX_DISPENSER_NANOLITER = 130000000;
private bool _diagnosticsSent;
private bool _eventsSent;
private bool _debugSent;
private bool _machineStatusSent;
private EmbeddedLogItem _last_embedded_debug_log;
private static RunningJobStatus _last_job_status;
public static String EmbeddedLogsFolder { get; private set; }
public static String EmbeddedLogsTag { get; private set; }
#region Constructors
///
/// Initializes the class.
///
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);
}
}
///
/// Initializes a new instance of the class.
///
public MachineOperator() : base()
{
DeviceInformation = new DeviceInformation();
MachineEventsStateProvider = new DefaultMachineEventsStateProvider();
JobRunsLogger = new BasicJobRunsLogger(this);
JobRunsLogger.Start();
EnableEventsNotification = true;
EnableMachineStatusUpdates = true;
EnableJobResume = true;
LogEmbeddedDebuggingToFile = true;
FirmwareUpgradeMode = FirmwareUpgradeModes.DFU | FirmwareUpgradeModes.TFP_PACKAGE;
GradientGenerationConfiguration = new DefaultGradientGenerationConfiguration();
EmergencyNotificationProvider = new UsbEmergencyNotificationProvider("COM1");
EnableJobLiquidQuantityValidation = true;
FailsWithAdapter = true;
}
///
/// Initializes a new instance of the class.
///
/// The transport adapter.
public MachineOperator(ITransportAdapter adapter) : this()
{
Adapter = adapter;
}
#endregion
#region Events
///
/// Occurs when the machine has changed.
///
public event EventHandler StatusChanged;
///
/// Occurs when there is new diagnostics data available.
///
public event EventHandler DiagnosticsDataAvailable;
///
/// Occurs when an events notification has been received from the embedded device.
///
public event EventHandler EventsNotification;
///
/// Occurs when a new debug log is available.
///
public event EventHandler DebugLogAvailable;
///
/// Occurs when machine embedded device status has changed.
///
public event EventHandler MachineStatusChanged;
///
/// Occurs when a new cartridge validation request has been received.
///
public event EventHandler CartridgeValidationRequestReceived;
///
/// 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;
///
/// Reports about the job printing preparation progress.
///
public event EventHandler PreparingJobProgress;
///
/// Occurs when a printing process has started.
///
public event EventHandler PrintingStarted;
///
/// Occurs when a printing process has completed.
///
public event EventHandler PrintingCompleted;
///
/// Occurs when a printing process has failed.
///
public event EventHandler PrintingFailed;
///
/// Occurs when a printing process has been aborted.
///
public event EventHandler PrintingAborted;
///
/// Occurs when a printing process has ended.
///
public event EventHandler PrintingEnded;
///
/// Occurs when the machine operator has detected that a job is in progress after connecting to the machine.
///
public event EventHandler ResumingJob;
#endregion
#region Properties
///
/// Gets or sets the job handling mode.
///
public JobHandlerModes JobHandlingMode { get; set; }
///
/// Gets or sets the job upload strategy.
///
public JobUploadStrategy JobUploadStrategy { get; set; }
///
/// Gets or sets the job number of units duplication method.
///
public JobUnitsMethods JobUnitsMethod { get; set; }
private MachineStatuses _status;
///
/// Gets the current machine status.
///
public MachineStatuses Status
{
get { return _status; }
protected set
{
if (_status != value)
{
_status = value;
RaisePropertyChangedAuto();
OnStatusChanged(value);
RaisePropertyChanged(nameof(IsPrinting));
RaisePropertyChanged(nameof(CanPrint));
LogManager.Log("Machine operator status changed: " + _status);
}
}
}
private MachineStatus _machineStatus;
///
/// Gets the machine embedded device status.
///
public MachineStatus MachineStatus
{
get { return _machineStatus; }
set { _machineStatus = value; RaisePropertyChangedAuto(); }
}
///
/// Gets or sets a value indicating whether to enable liquid quantity validation before starting the job.
/// The validation is done using the reported .
///
public bool EnableJobLiquidQuantityValidation { get; set; }
///
/// Gets or sets the firmware upgrade mode.
///
public FirmwareUpgradeModes FirmwareUpgradeMode { get; set; }
///
/// Gets a value indicating whether this instance is printing.
///
public bool IsPrinting
{
get
{
return Status == MachineStatuses.Printing || Status == MachineStatuses.GettingReady;
}
}
///
/// Gets a value indicating whether this instance can print.
///
public bool CanPrint
{
get
{
return Status == MachineStatuses.ReadyToDye;
}
}
private Job _runningJob;
///
/// Gets the running job.
///
public Job RunningJob
{
get { return _runningJob; }
set { _runningJob = value; RaisePropertyChangedAuto(); }
}
private RunningJobStatus _runningJobStatus;
///
/// Gets the running job status.
///
public RunningJobStatus RunningJobStatus
{
get { return _runningJobStatus; }
set { _runningJobStatus = value; RaisePropertyChangedAuto(); }
}
///
/// 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 _enableEventsNotification;
///
/// Gets or sets a value indicating whether direct the embedded device to send events notification messages.
///
public bool EnableEventsNotification
{
get { return _enableEventsNotification; }
set
{
if (_enableEventsNotification != value)
{
_enableEventsNotification = value;
RaisePropertyChangedAuto();
OnEnableEventsNotification(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 _enableMachineStatusUpdates;
///
/// Gets or sets a value indicating whether to direct the embedded device to update about status changes.
///
public bool EnableMachineStatusUpdates
{
get { return _enableMachineStatusUpdates; }
set
{
if (_enableMachineStatusUpdates != value)
{
_enableMachineStatusUpdates = value;
RaisePropertyChangedAuto();
OnEnableMachineStatusUpdatesChanged(value);
}
}
}
private bool _enableJobResume;
///
/// Gets or sets a value indicating whether to check whether a job is in progress after connection was successful.
///
public bool EnableJobResume
{
get
{
return _enableJobResume;
}
set
{
_enableJobResume = value; RaisePropertyChangedAuto();
}
}
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 or sets the job runs logger.
///
public IJobRunsLogger JobRunsLogger { 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(); }
}
private IGradientGenerationConfiguration _gradientGenerationConfiguration;
///
/// Gets or sets the gradients generation configuration.
///
public IGradientGenerationConfiguration GradientGenerationConfiguration
{
get { return _gradientGenerationConfiguration; }
set { _gradientGenerationConfiguration = value; RaisePropertyChangedAuto(); }
}
///
/// Gets or sets the emergency notification provider.
///
public IEmergencyNotificationProvider EmergencyNotificationProvider { get; set; }
#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;
if (!(ex is ContinuousResponseAbortedException))
{
LogRequestFailed(request, ex);
}
},
() =>
{
_diagnosticsSent = false;
LogManager.Log("Diagnostics response completed!?", LogCategory.Warning);
});
LogRequestSent(request);
}
else if (_diagnosticsSent)
{
_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 events property has been changed.
///
/// if set to true [value].
protected virtual async void OnEnableEventsNotification(bool value)
{
if (value && State == TransportComponentState.Connected && !_eventsSent)
{
var request = new StartEventsNotificationRequest();
bool responseLogged = false;
_eventsSent = true;
SendContinuousRequest(request).ObserveOn(new NewThreadScheduler()).Subscribe(
(response) =>
{
OnEventsNotification(response);
if (!responseLogged)
{
LogResponseReceived(response.Message);
responseLogged = true;
}
},
(ex) =>
{
_eventsSent = false;
if (!(ex is ContinuousResponseAbortedException))
{
LogRequestFailed(request, ex);
}
},
() =>
{
_eventsSent = false;
LogManager.Log("Events Notification response completed!?", LogCategory.Warning);
});
LogRequestSent(request);
}
else if (_eventsSent)
{
_eventsSent = false;
if (State == TransportComponentState.Connected)
{
var req = new StopEventsNotificationRequest();
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 virtual 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;
if (!(ex is ContinuousResponseAbortedException))
{
LogRequestFailed(request, ex);
}
},
() =>
{
_debugSent = false;
});
LogRequestSent(request);
}
else if (_debugSent)
{
_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);
}
}
}
}
///
/// Called when the enable machine status updates has been changed.
///
/// if set to true [value].
protected virtual async void OnEnableMachineStatusUpdatesChanged(bool value)
{
if (value && State == TransportComponentState.Connected && !_machineStatusSent)
{
var request = new StartMachineStatusUpdateRequest();
bool responseLogged = false;
_machineStatusSent = true;
SendContinuousRequest(request).ObserveOn(new NewThreadScheduler()).Subscribe(
(response) =>
{
OnMachineStatusChanged(response);
if (!responseLogged)
{
LogResponseReceived(response.Message);
responseLogged = true;
}
},
(ex) =>
{
_machineStatusSent = false;
if (!(ex is ContinuousResponseAbortedException))
{
LogRequestFailed(request, ex);
}
},
() =>
{
_machineStatusSent = false;
LogManager.Log("Machine status update response completed!?", LogCategory.Warning);
});
LogRequestSent(request);
}
else if (_machineStatusSent)
{
_machineStatusSent = false;
if (State == TransportComponentState.Connected)
{
var req = new StopMachineStatusUpdateRequest();
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)
{
DiagnosticsDataAvailable?.Invoke(this, data);
}
///
/// Called when events notification message has been received.
///
/// The response.
protected virtual void OnEventsNotification(StartEventsNotificationResponse response)
{
if (MachineEventsStateProvider != null)
{
MachineEventsStateProvider.ApplyEvents(response.Events);
}
EventsNotification?.Invoke(this, response);
}
///
/// Invokes the event.
///
/// The sensors data.
protected virtual void OnDebugLogAvailable(StartDebugLogResponse data)
{
if (_last_embedded_debug_log == null || _last_embedded_debug_log.DebugLogResponse.Message != data.Message)
{
_last_embedded_debug_log = new EmbeddedLogItem(data);
if (LogEmbeddedDebuggingToFile && EmbeddedLogManager != null)
{
EmbeddedLogManager.Log(_last_embedded_debug_log);
}
DebugLogAvailable?.Invoke(this, data);
}
else
{
_last_embedded_debug_log.Repeated++;
if (LogEmbeddedDebuggingToFile && EmbeddedLogManager != null)
{
EmbeddedLogManager.Log(new EmbeddedLogItem(data));
}
}
}
///
/// Called when the machine status has been update
///
/// The response.
protected virtual void OnMachineStatusChanged(StartMachineStatusUpdateResponse response)
{
if (response.Status == null) return;
bool changed = (MachineStatus == null || response.Status.State != MachineStatus.State);
MachineStatus = response.Status;
MachineStatusChanged?.Invoke(this, MachineStatus);
if (changed)
{
LogManager.Log($"Machine State Changed: {MachineStatus.State}.");
switch (MachineStatus.State)
{
case MachineState.Initializing:
Status = MachineStatuses.Service;
break;
//case MachineState.PreparingJob:
// Status = MachineStatuses.GettingReady;
// break;
case MachineState.Ready:
Status = MachineStatuses.ReadyToDye;
break;
//case MachineState.Sleep:
// Status = MachineStatuses.Standby;
// break;
case MachineState.PowerOff:
Status = MachineStatuses.ShuttingDown;
break;
case MachineState.Error:
Status = MachineStatuses.Error;
break;
}
}
}
///
/// 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 a new request has been received.
///
/// The request.
protected override void OnRequestReceived(MessageContainer container)
{
base.OnRequestReceived(container);
if (container.Type == MessageType.CartridgeValidationRequest)
{
OnCartridgeValidationRequestReceived(container.Token, MessageFactory.ExtractMessageFromContainer(container));
}
else if (container.Type == MessageType.UpdateStatusRequest)
{
OnUpdateStatusRequestReceived(container.Token, MessageFactory.ExtractMessageFromContainer(container));
}
}
///
/// 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));
}
///
/// Called when the machine status has been changed
///
/// The status.
protected virtual void OnStatusChanged(MachineStatuses status)
{
StatusChanged?.Invoke(this, status);
}
///
/// Called when the cartridge validation request has been received.
///
/// The request.
protected virtual void OnCartridgeValidationRequestReceived(String token, CartridgeValidationRequest request)
{
if (request.Action == CartridgeAction.Inserted)
{
CartridgeValidationEventArgs e = new CartridgeValidationEventArgs(request, (index) =>
{
//Approve
SendResponse(new CartridgeValidationResponse()
{
IsValid = true,
Index = index,
}, token).Wait();
}, () =>
{
//Decline
SendResponse(new CartridgeValidationResponse()
{
}, token).Wait();
});
CartridgeValidationRequestReceived?.Invoke(this, e);
}
}
///
/// Called when the update status request has been received.
///
/// The token.
/// The update status request.
protected virtual void OnUpdateStatusRequestReceived(string token, UpdateStatusRequest request)
{
try
{
Status = (MachineStatuses)request.Status;
}
catch (Exception ex)
{
LogManager.Log(ex);
}
SendResponse(new UpdateStatusResponse());
}
#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;
_eventsSent = false;
_machineStatusSent = false;
Status = MachineStatuses.Disconnected;
}
}
///
/// Disconnects the machine operator and the underlying transporter.
///
///
public async override Task Disconnect()
{
if (Status == MachineStatuses.Upgrading) return;
if (State == TransportComponentState.Connected)
{
DisconnectRequest request = new DisconnectRequest();
LogRequestSent(request);
try
{
var response = await SendRequest(request);
LogResponseReceived(response.Message);
Status = MachineStatuses.Disconnected;
}
catch (Exception ex)
{
LogRequestFailed(request, ex);
}
}
if (MachineEventsStateProvider != null)
{
MachineEventsStateProvider.Reset();
}
await base.Disconnect();
}
///
/// Connects the transport component.
///
///
public async override Task Connect()
{
var keep_alive = UseKeepAlive;
UseKeepAlive = false;
if (Status != MachineStatuses.Upgrading)
{
await base.Connect();
}
if (State == TransportComponentState.Connected)
{
ConnectRequest request = new ConnectRequest()
{
Password = "1234",
UnixTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
};
LogRequestSent(request);
try
{
var response = await SendRequest(request);
LogResponseReceived(response.Message);
if (Status != MachineStatuses.Upgrading)
{
Status = MachineStatuses.ReadyToDye;
}
DeviceInformation = response.Message.DeviceInformation;
_diagnosticsSent = false;
_eventsSent = false;
_debugSent = false;
_machineStatusSent = false;
OnEnableDiagnosticsChanged(EnableDiagnostics);
OnEnableEmbeddedDebuggingChanged(EnableEmbeddedDebugging);
OnEnableEventsNotification(EnableEventsNotification);
OnEnableMachineStatusUpdatesChanged(EnableMachineStatusUpdates);
if (EnableJobResume)
{
ResumeJob();
}
}
catch (Exception ex)
{
LogRequestFailed(request, ex);
await base.Disconnect();
throw ex;
}
finally
{
UseKeepAlive = keep_alive;
}
}
}
#endregion
#region Private Methods
private async void ResumeJob()
{
LogManager.Log("Checking if a job is in progress...");
try
{
var res = await SendRequest(new CurrentJobRequest());
if (res.Message.IsJobInProgress)
{
JobTicket jobTicket = res.Message.JobTicket;
ProcessParametersTable processParameters = new ProcessParametersTable();
jobTicket.ProcessParameters.MapPrimitivesTo(processParameters);
ResumingJobEventArgs args = new ResumingJobEventArgs((job) =>
{
if (Status != MachineStatuses.ReadyToDye)
{
throw new InvalidOperationException("Could not print while status = " + Status);
}
RunningJob = null;
RunningJobStatus = null;
var originalJob = job;
CurrentProcessParameters = processParameters;
var request = new ResumeCurrentJobRequest();
JobHandler handler = null;
handler = new JobHandler(async () =>
{
try
{
var result = await SendRequest(new AbortJobRequest());
PrintingAborted?.Invoke(this, new PrintingEventArgs(handler, originalJob));
PrintingEnded?.Invoke(this, new PrintingEventArgs(handler, originalJob));
handler.RaiseCanceled();
}
catch (Exception ex)
{
LogManager.Log(ex, "Failed to cancel job.");
}
}, originalJob, jobTicket, processParameters, JobHandlingMode);
handler.StatusChanged += (x, s) =>
{
RunningJobStatus = s;
};
LogRequestSent(request);
bool responseLogged = false;
Thread.Sleep(500); //Just wait maybe Shlomo is getting this message to fast after restart ?
bool completed = false;
SendContinuousRequest(request, null, TimeSpan.FromSeconds(2)).Subscribe((response) =>
{
if (!completed)
{
if (!responseLogged)
{
if (_last_job_status != null)
{
_last_job_status.IsCanceled = false;
_last_job_status.IsCompleted = false;
_last_job_status.IsFailed = false;
handler.Status = _last_job_status;
}
}
handler.RaiseStatusReceived(response.Message.Status);
if (!responseLogged)
{
Status = MachineStatuses.GettingReady;
responseLogged = true;
RunningJob = originalJob;
PrintingStarted?.Invoke(this, new PrintingEventArgs(handler, originalJob));
LogResponseReceived(response.Message);
}
if (JobHandlingMode == JobHandlerModes.SettingUp)
{
if (response.Message.Status.Progress > processParameters.DryerBufferLengthMeters)
{
if (!completed)
{
Status = MachineStatuses.Printing;
}
}
}
else
{
if (response.Message.Status.Progress > 0)
{
if (!completed)
{
Status = MachineStatuses.Printing;
}
}
}
}
}, (ex) =>
{
if (!completed)
{
completed = true;
if (!(ex is ContinuousResponseAbortedException))
{
Status = MachineStatuses.ReadyToDye;
if (!handler.IsCanceled)
{
PrintingFailed?.Invoke(this, new PrintingFailedEventArgs(handler, originalJob, ex));
PrintingEnded?.Invoke(this, new PrintingEventArgs(handler, originalJob));
handler.RaiseFailed(ex);
LogRequestFailed(request, ex);
}
}
else
{
Status = MachineStatuses.ReadyToDye;
}
}
}, () =>
{
if (!completed)
{
completed = true;
Status = MachineStatuses.ReadyToDye;
PrintingCompleted?.Invoke(this, new PrintingEventArgs(handler, originalJob));
PrintingEnded?.Invoke(this, new PrintingEventArgs(handler, originalJob));
handler.RaiseCompleted();
}
});
return handler;
});
args.JobGuid = jobTicket.Guid;
ResumingJob?.Invoke(this, args);
}
}
catch (Exception ex)
{
LogManager.Log(ex);
}
}
///
/// Logs the request sent.
///
/// The message.
protected void LogRequestSent(IMessage message)
{
if (!(message is FileChunkUploadRequest) && !(message is FileDownloadRequest))
{
LogManager.Log(String.Format("Sending request '{0}'...{1}{2}", message.GetType().Name, Environment.NewLine, message.ToJsonString()));
OnRequestSent(message);
}
}
///
/// Logs the request failed.
///
/// The message.
protected 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.
protected void LogResponseReceived(IMessage message)
{
if (!(message is FileChunkUploadResponse) && !(message is FileDownloadResponse))
{
LogManager.Log(String.Format("Response received '{0}'...{1}{2}", message.GetType().Name, Environment.NewLine, message.ToJsonString()));
OnResponseReceived(message);
}
}
///
/// Creates a PMR job segment.
///
/// The segment.
///
private JobSegment CreatePMRJobSegment(Segment segment, Job job, ProcessParametersTable processParameters)
{
LogManager.Log($"Converting segment {segment.SegmentIndex} to PMR segment...");
JobSegment jobSegment = new JobSegment();
jobSegment.Length = segment.LengthWithFactor;
jobSegment.Name = segment.Name;
var stops = segment.BrushStops.ToList();
if (GradientGenerationConfiguration != null && GradientGenerationConfiguration.IsEnabled && segment.BrushStops.Count > 1)
{
LogManager.Log($"Generate segment {segment.SegmentIndex} gradient...");
stops = GradientGenerationConfiguration.Generate(segment, job, processParameters, (e) =>
{
PreparingJobProgress?.Invoke(this, e);
});
LogManager.Log($"Gradient generated.");
}
foreach (var stop in stops)
{
JobBrushStop jobStop = new JobBrushStop();
jobStop.Index = stop.StopIndex;
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;
if (liquidVolume.DispenserStepDivision != BL.Dispensing.DispenserStepDivisions.Auto)
{
dispenser.NanoliterPerPulse = liquidVolume.NanoliterPerStep;
}
else
{
dispenser.NanoliterPerPulse = liquidVolume.IdsPack.Dispenser.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);
}
return jobSegment;
}
private void ContinueSingleSpoolJob(Segment segment, Job job, ProcessParametersTable processParameters, JobHandler handler)
{
JobRequest request = new JobRequest();
JobTicket ticket = new JobTicket();
ticket.Guid = handler.Job.Guid;
ticket.EnableInterSegment = job.EnableInterSegment;
ticket.InterSegmentLength = job.InterSegmentLength;
ticket.Length = segment.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;
ticket.Segments.Add(CreatePMRJobSegment(segment, job, processParameters));
request.JobTicket = ticket;
LogRequestSent(request);
bool responseLogged = false;
var previous_segments_length = job.Segments.Where(x => x.SegmentIndex < segment.SegmentIndex).Sum(x => x.Length);
SendContinuousRequest(request, null, TimeSpan.FromSeconds(2)).Subscribe((response) =>
{
response.Message.Status.Progress += previous_segments_length;
handler.RaiseStatusReceived(response.Message.Status);
if (!responseLogged && segment == job.OrderedSegments.First())
{
responseLogged = true;
Status = MachineStatuses.Printing;
RunningJob = handler.Job;
PrintingStarted?.Invoke(this, new PrintingEventArgs(handler, handler.Job));
LogResponseReceived(response.Message);
}
}, (ex) =>
{
if (!(ex is ContinuousResponseAbortedException))
{
Status = MachineStatuses.ReadyToDye;
if (!handler.IsCanceled)
{
PrintingFailed?.Invoke(this, new PrintingFailedEventArgs(handler, handler.Job, ex));
PrintingEnded?.Invoke(this, new PrintingEventArgs(handler, handler.Job));
handler.RaiseFailed(ex);
LogRequestFailed(request, ex);
}
}
else
{
Status = MachineStatuses.ReadyToDye;
}
}, () =>
{
if (segment == job.OrderedSegments.Last())
{
Status = MachineStatuses.ReadyToDye;
PrintingCompleted?.Invoke(this, new PrintingEventArgs(handler, handler.Job));
PrintingEnded?.Invoke(this, new PrintingEventArgs(handler, handler.Job));
handler.RaiseCompleted();
}
else
{
handler.RaiseSpoolChangeRequired(() =>
{
ContinueSingleSpoolJob(segment.GetNextSegment(), job, processParameters, handler);
}, () =>
{
PrintingAborted?.Invoke(this, new PrintingEventArgs(handler, handler.Job));
PrintingEnded?.Invoke(this, new PrintingEventArgs(handler, handler.Job));
Status = MachineStatuses.ReadyToDye;
handler.RaiseCanceled();
});
}
});
}
private void ValidateJobLiquidQuantity(Job job, ProcessParametersTable processParameters, Configuration configuration)
{
Dictionary liquidQuantities = new Dictionary();
foreach (var pack in configuration.NoneEmptyIdsPacks.OrderBy(x => x.PackIndex))
{
liquidQuantities.Add(pack.PackIndex, 0);
}
for (int i = 0; i < Math.Max(job.NumberOfUnits, 1); i++)
{
for (int segmentIndex = 0; segmentIndex < job.Segments.Count; segmentIndex++)
{
var segment = job.Segments[segmentIndex];
var segment_length_cm = segment.Length * 100d;
var stop_count = segment.BrushStops.Count - (segment.BrushStops.Count == 1 ? 0 : 1);
var stop_length_centimeters = segment_length_cm / stop_count;
for (int stopIndex = 0; stopIndex < stop_count; stopIndex++)
{
var stop = segment.BrushStops[stopIndex];
foreach (var liquidVolumes in stop.LiquidVolumes)
{
liquidQuantities[liquidVolumes.IdsPack.PackIndex] += liquidVolumes.NanoliterPerCentimeter * stop_length_centimeters;
}
}
}
}
if (MachineStatus != null)
{
var exception = new InsufficientLiquidQuantityException($"Insufficient liquids level.");
bool shouldThrow = false;
foreach (var liquidQuantity in liquidQuantities)
{
int index = liquidQuantity.Key;
var packLevel = MachineStatus.IDSPacksLevels.SingleOrDefault(x => x.Index == index);
var idsPack = configuration.NoneEmptyIdsPacks.SingleOrDefault(x => x.PackIndex == index);
if (packLevel != null)
{
var idsLevel = new InsufficientLiquidQuantityException.IDSPackLevel()
{
IdsPack = idsPack,
Current = packLevel.DispenserLevel,
Required = (int)liquidQuantities[index]
};
if (liquidQuantities[index] > packLevel.DispenserLevel)
{
shouldThrow = true;
}
exception.IdsPackLevels.Add(idsLevel);
}
else
{
LogManager.Log($"Could not validate required liquid quantity for job. Missing IDS Pack level at index {index}.", LogCategory.Warning);
}
}
if (shouldThrow)
{
throw LogManager.Log(exception, JsonConvert.SerializeObject(exception.IdsPackLevels.Select(x => new
{
Liquid = x.IdsPack.LiquidType.Name,
x.Required,
x.Current
}).ToList()));
}
}
else
{
LogManager.Log("Could not validate required liquid quantity for job. No machine status received", LogCategory.Warning);
}
}
#endregion
#region Public Methods
///
/// Prints the specified job.
/// The process parameters table will be calculated using color conversion gamut region.
/// This method cannot accept brush stops with 'Volume' as color space.
///
/// The job.
///
public Task Print(Job job)
{
IColorConverter converter = new DefaultColorConverter();
var jobSegments = job.OrderedSegments;
if (job.Rml == null)
{
throw new NullReferenceException("Job RML is null");
}
var processGroup = job.Rml.ProcessParametersTablesGroups.FirstOrDefault(x => x.Active);
if (processGroup == null)
{
throw new NullReferenceException("Could not locate an active process parameters tables group for RML " + job.Rml.Name);
}
var processParameters = converter.GetRecommendedProcessParameters(job);
if (processParameters == null)
{
throw new NullReferenceException("Could not locate any process parameters table in group " + processGroup.Name + " for RML " + job.Rml.Name);
}
//Perform color correction
foreach (var stop in jobSegments.SelectMany(x => x.BrushStops))
{
if (stop.LiquidVolumes == null)
{
if (stop.BrushColorSpace == ColorSpaces.RGB || stop.BrushColorSpace == ColorSpaces.LAB)
{
var output = converter.Convert(stop, false);
//TODO: Restore this when Mirta conversion is working as expected.
//if (suggestions.OutOfGamut)
//{
// throw new InvalidOperationException("Cannot print a brush stop which is out of gamut.");
//}
stop.SetLiquidVolumes(job.Machine.Configuration, job.Rml, processParameters);
foreach (var outputLiquid in output.SingleCoordinates.OutputLiquids)
{
var liquidVolume = stop.LiquidVolumes.SingleOrDefault(x => x.IdsPack.LiquidType.Code == outputLiquid.LiquidType.ToInt32());
if (liquidVolume == null)
{
throw new NullReferenceException("Liquid volume not found for color conversion output liquid '" + outputLiquid.LiquidType + "'.");
}
liquidVolume.Volume = outputLiquid.Volume;
}
}
else if (stop.BrushColorSpace == ColorSpaces.Catalog)
{
if (stop.ColorCatalogsItem != null)
{
stop.SetLiquidVolumes(job.Machine.Configuration, job.Rml, processParameters);
if (stop.ColorCatalogsItem.Cyan > 0)
{
var liquidVolume = stop.LiquidVolumes.SingleOrDefault(x => x.IdsPack.LiquidType.Code == LiquidTypes.Cyan.ToInt32());
if (liquidVolume == null)
{
throw new NullReferenceException("Liquid volume not found for color conversion output liquid '" + LiquidTypes.Cyan + "'.");
}
liquidVolume.Volume = stop.ColorCatalogsItem.Cyan;
}
if (stop.ColorCatalogsItem.Magenta > 0)
{
var liquidVolume = stop.LiquidVolumes.SingleOrDefault(x => x.IdsPack.LiquidType.Code == LiquidTypes.Magenta.ToInt32());
if (liquidVolume == null)
{
throw new NullReferenceException("Liquid volume not found for color conversion output liquid '" + LiquidTypes.Magenta + "'.");
}
liquidVolume.Volume = stop.ColorCatalogsItem.Magenta;
}
if (stop.ColorCatalogsItem.Yellow > 0)
{
var liquidVolume = stop.LiquidVolumes.SingleOrDefault(x => x.IdsPack.LiquidType.Code == LiquidTypes.Yellow.ToInt32());
if (liquidVolume == null)
{
throw new NullReferenceException("Liquid volume not found for color conversion output liquid '" + LiquidTypes.Yellow + "'.");
}
liquidVolume.Volume = stop.ColorCatalogsItem.Yellow;
}
if (stop.ColorCatalogsItem.Black > 0)
{
var liquidVolume = stop.LiquidVolumes.SingleOrDefault(x => x.IdsPack.LiquidType.Code == LiquidTypes.Black.ToInt32());
if (liquidVolume == null)
{
throw new NullReferenceException("Liquid volume not found for color conversion output liquid '" + LiquidTypes.Black + "'.");
}
liquidVolume.Volume = stop.ColorCatalogsItem.Black;
}
}
else
{
throw new InvalidOperationException($"No catalog item specified for segment color.");
}
}
else if (stop.BrushColorSpace == ColorSpaces.Volume)
{
stop.SetLiquidVolumes(job.Machine.Configuration, job.Rml, processParameters);
}
else
{
throw new InvalidOperationException($"Unsupported color space {stop.BrushColorSpace}.");
}
}
if (job.EnableLubrication)
{
var lubricantVolume = stop.LiquidVolumes.SingleOrDefault(x => x.IdsPack != null && x.IdsPack.LiquidType != null && x.IdsPack.LiquidType.Code == LiquidTypes.Lubricant.ToInt32());
if (lubricantVolume != null)
{
lubricantVolume.Volume = 100;
}
}
}
return Print(job, processParameters);
}
///
/// Prints the specified job using the specified job parameters.
///
/// The job.
/// Process parameters table
///
public Task Print(Job job, ProcessParametersTable processParameters)
{
return Task.Factory.StartNew(() =>
{
if (Status != MachineStatuses.ReadyToDye)
{
throw new InvalidOperationException("Could not print while status = " + Status);
}
LogManager.Log($"Executing job '{job.Name}'...");
RunningJob = null;
RunningJobStatus = null;
if (job.NumberOfUnits < 1)
{
job.NumberOfUnits = 1;
}
if (EnableJobLiquidQuantityValidation)
{
ValidateJobLiquidQuantity(job, processParameters, job.Machine.Configuration);
}
var originalJob = job;
var clonedJob = job.Clone();
clonedJob.Guid = job.Guid;
clonedJob.Name = job.Name;
CurrentProcessParameters = processParameters;
JobRequest request = new JobRequest();
job = job.Clone();
job.Guid = originalJob.Guid;
job.Name = originalJob.Name;
int max = job.OrderedSegments.Last().SegmentIndex + 1;
var segments = job.OrderedSegments.ToList();
for (int i = 0; i < job.NumberOfUnits - 1; i++)
{
foreach (var s in segments)
{
var cloned = s.Clone(job);
cloned.SegmentIndex = max++;
job.Segments.Add(cloned);
}
}
JobTicket ticket = new JobTicket();
ticket.Guid = originalJob.Guid;
ticket.EnableInterSegment = job.EnableInterSegment;
ticket.InterSegmentLength = Math.Max(job.InterSegmentLength, 1);
ticket.EnableLubrication = job.EnableLubrication;
ticket.Length = job.Length;
ticket.WindingMethod = (JobWindingMethod)job.WindingMethod.Code;
if (JobUnitsMethod == JobUnitsMethods.Device)
{
ticket.NumberOfUnits = (uint)job.NumberOfUnits;
}
ticket.Spool = new JobSpool();
job.SpoolType.MapPrimitivesTo(ticket.Spool);
var spool = job.Machine.Spools.SingleOrDefault(x => x.SpoolType == job.SpoolType);
if (spool == null)
{
throw new InvalidOperationException("Job spool type is not registered with this machine.");
}
else
{
spool.MapPrimitivesTo(ticket.Spool);
}
ticket.Spool.JobSpoolType = (JobSpoolType)job.SpoolType.Code;
ProcessParameters process = new ProcessParameters();
processParameters.MapPrimitivesTo(process);
ticket.ProcessParameters = process;
JobHandler handler = null;
bool canceled = false;
bool requestSent = false;
handler = new JobHandler(async () =>
{
try
{
if (!canceled)
{
canceled = true;
LogManager.Log($"Aborting current gradient generation...");
GradientGenerationConfiguration.AbortCurrentGeneration();
if (requestSent)
{
var result = await SendRequest(new AbortJobRequest());
}
PrintingAborted?.Invoke(this, new PrintingEventArgs(handler, clonedJob));
PrintingEnded?.Invoke(this, new PrintingEventArgs(handler, clonedJob));
handler.RaiseCanceled();
}
}
catch (Exception ex)
{
LogManager.Log(ex, "Failed to cancel job.");
}
}, clonedJob, ticket, processParameters, JobHandlingMode);
handler.StatusChanged += (x, s) =>
{
RunningJobStatus = s;
};
if (!job.IsAllSegmentsPerSpool)
{
ContinueSingleSpoolJob(job.OrderedSegments.First(), job, processParameters, handler);
return handler;
}
ThreadFactory.StartNew(async () =>
{
Status = MachineStatuses.GettingReady;
RunningJob = clonedJob;
PrintingStarted?.Invoke(this, new PrintingEventArgs(handler, clonedJob));
Thread.Sleep(100);
handler.RaiseStatusReceived(new JobStatus()
{
CurrentSegmentIndex = 0,
Progress = 0,
Message = "Preparing Job...",
});
foreach (var segment in originalJob.OrderedSegments)
{
try
{
ticket.Segments.Add(CreatePMRJobSegment(segment, originalJob, processParameters));
}
catch (Exception ex)
{
handler.RaiseFailed(ex);
Status = MachineStatuses.ReadyToDye;
return;
}
if (handler.IsCanceled)
{
Status = MachineStatuses.ReadyToDye;
return;
}
}
if (handler.IsCanceled)
{
Status = MachineStatuses.ReadyToDye;
return;
}
var segs = new List();
if (JobUnitsMethod == JobUnitsMethods.Operator)
{
for (int i = 0; i < job.NumberOfUnits; i++)
{
foreach (var s in ticket.Segments)
{
var cloned = s.Clone();
segs.Add(cloned);
}
}
}
else
{
foreach (var s in ticket.Segments)
{
var cloned = s.Clone();
segs.Add(cloned);
}
}
if (segs.Count > 0)
{
ticket.Segments.Clear();
ticket.Segments.AddRange(segs);
}
request.JobTicket = ticket.Clone();
request.JobTicket.UploadStrategy = JobUploadStrategy;
//Use this if you want to log the entire job...
var logRequest = request.Clone();
LogManager.Log($"Job upload method is set to {JobUploadStrategy}...");
var oldKeepAlive = UseKeepAlive;
if (JobUploadStrategy == JobUploadStrategy.JobDescriptionFile)
{
LogManager.Log("Generating job description file...");
try
{
request.JobTicket.Segments.Clear();
JobDescriptionFile jobDescriptionFile = new JobDescriptionFile(ticket.Segments);
MemoryStream ms = jobDescriptionFile.ToStream();
handler.RaiseStatusReceived(new JobStatus()
{
CurrentSegmentIndex = 0,
Progress = 0,
Message = "Uploading job description file...",
});
LogManager.Log("Creating storage API manager...");
var storage = CreateStorageManager();
//Suppress keep alive while job uploads.
//storage.SuppressKeepAliveWhileFileUploads = true;
UseKeepAlive = false; //This is a work around for Shlomo not managing to keep alive while parsing the file.
LogManager.Log("Getting storage drive information...");
var storageInfo = await storage.GetStorageDrive();
LogManager.Log("Getting root folder information...");
var root_folder = await storage.GetRootFolder();
var existing_item = root_folder.Items.SingleOrDefault(x => x.Name == JOB_DESCRIPTION_FILE_NAME);
if (existing_item != null)
{
LogManager.Log("Removing previous job description file...");
await storage.DeleteItem(existing_item);
}
String job_file_path = Path.Combine(storageInfo.Root, JOB_DESCRIPTION_FILE_NAME);
LogManager.Log($"Uploading job description file '{job_file_path}' of size: {ms.Length}");
await storage.UploadFileSync(job_file_path, ms);
LogManager.Log("Job upload completed successfully.");
ms.Dispose();
request.JobTicket.JobDescriptionFile = job_file_path;
}
catch (Exception ex)
{
UseKeepAlive = oldKeepAlive;
Status = MachineStatuses.ReadyToDye;
PrintingFailed?.Invoke(this, new PrintingFailedEventArgs(handler, clonedJob, ex));
PrintingEnded?.Invoke(this, new PrintingEventArgs(handler, clonedJob));
handler.RaiseFailed(ex);
LogRequestFailed(request, ex);
return;
}
}
if (handler.IsCanceled)
{
UseKeepAlive = oldKeepAlive;
Status = MachineStatuses.ReadyToDye;
return;
}
LogRequestSent(request);
bool responseLogged = false;
bool completed = false; //Use this in case Shlomo is sending progress after completion.
SendContinuousRequest(request, null, TimeSpan.FromSeconds(2)).Subscribe((response) =>
{
if (!completed)
{
handler.RaiseStatusReceived(response.Message.Status);
_last_job_status = handler.Status;
if (response.Message.Status.Progress > 0)
{
if (oldKeepAlive != UseKeepAlive)
{
UseKeepAlive = oldKeepAlive;
}
}
if (!responseLogged)
{
requestSent = true;
responseLogged = true;
LogResponseReceived(response.Message);
}
if (JobHandlingMode == JobHandlerModes.SettingUp)
{
if (response.Message.Status.Progress > processParameters.DryerBufferLengthMeters)
{
if (!completed)
{
Status = MachineStatuses.Printing;
}
}
}
else
{
if (response.Message.Status.Progress > 0)
{
if (!completed)
{
Status = MachineStatuses.Printing;
}
}
}
}
}, (ex) =>
{
if (!completed)
{
completed = true;
UseKeepAlive = oldKeepAlive;
if (!(ex is ContinuousResponseAbortedException))
{
Status = MachineStatuses.ReadyToDye;
if (!handler.IsCanceled)
{
PrintingFailed?.Invoke(this, new PrintingFailedEventArgs(handler, clonedJob, ex));
PrintingEnded?.Invoke(this, new PrintingEventArgs(handler, clonedJob));
handler.RaiseFailed(ex);
LogRequestFailed(request, ex);
}
}
else
{
Status = MachineStatuses.ReadyToDye;
}
}
}, () =>
{
if (!completed)
{
completed = true;
UseKeepAlive = oldKeepAlive;
Status = MachineStatuses.ReadyToDye;
PrintingCompleted?.Invoke(this, new PrintingEventArgs(handler, clonedJob));
PrintingEnded?.Invoke(this, new PrintingEventArgs(handler, clonedJob));
handler.RaiseCompleted();
}
});
});
return handler;
});
}
///
/// Executes a print stub for emulating a full job.
/// The process parameters table will be calculated using color conversion gamut region.
/// This method cannot accept brush stops with 'Volume' as color space.
///
/// The job.
///
///
/// Cannot print a brush stop with volume color space when process parameters table has not been specified.
/// or
/// Could not print while status = " + Status
///
///
/// Job RML is null
/// or
/// Could not locate an active process parameters tables group for RML " + job.Rml.Name
/// or
/// Could not locate process parameters table index " + processParametersTableIndex + " in group " + processGroup.Name + " for RML " + job.Rml.Name
/// or
/// Liquid volume not found for color conversion output liquid '" + outputLiquid.LiquidType + "'.
///
public Task PrintStub(Job job)
{
return Task.Factory.StartNew(() =>
{
//Check not brush stop has color space 'Volume'.
if (job.Segments.SelectMany(x => x.BrushStops).ToList().Exists(x => x.ColorSpace.Code == ColorSpaces.Volume.ToInt32()))
{
throw new InvalidOperationException("Cannot print a brush stop with volume color space when process parameters table has not been specified.");
}
//Get least common process parameters table index.
int processParametersTableIndex = 0;
if (job.Rml == null)
{
throw new NullReferenceException("Job RML is null");
}
var processGroup = job.Rml.ProcessParametersTablesGroups.FirstOrDefault(x => x.Active);
if (processGroup == null)
{
throw new NullReferenceException("Could not locate an active process parameters tables group for RML " + job.Rml.Name);
}
var processParameters = processGroup.ProcessParametersTables.FirstOrDefault(x => x.TableIndex == processParametersTableIndex);
if (processParameters == null)
{
throw new NullReferenceException("Could not locate process parameters table index " + processParametersTableIndex + " in group " + processGroup.Name + " for RML " + job.Rml.Name);
}
//Perform color correction
foreach (var stop in job.Segments.SelectMany(x => x.BrushStops))
{
if (stop.LiquidVolumes == null)
{
stop.SetLiquidVolumes(job.Machine.Configuration, job.Rml, processParameters);
}
foreach (var liquidVolume in stop.LiquidVolumes)
{
liquidVolume.Volume = 10;
}
}
if (Status != MachineStatuses.ReadyToDye)
{
throw new InvalidOperationException("Could not print while status = " + Status);
}
RunningJob = null;
RunningJobStatus = null;
var originalJob = job;
CurrentProcessParameters = processParameters;
StubJobRequest request = new StubJobRequest();
if (job.NumberOfUnits < 1)
{
job.NumberOfUnits = 1;
}
job = job.Clone();
var segments = job.OrderedSegments.ToList();
for (int i = 0; i < job.NumberOfUnits - 1; i++)
{
foreach (var s in segments)
{
job.Segments.Add(s);
}
}
JobTicket ticket = new JobTicket();
ticket.Guid = originalJob.Guid;
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.OrderedSegments)
{
JobSegment jobSegment = new JobSegment();
jobSegment.Length = segment.LengthWithFactor;
jobSegment.Name = segment.Name;
foreach (var stop in segment.BrushStops)
{
JobBrushStop jobStop = new JobBrushStop();
jobStop.Index = stop.StopIndex;
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.Dispenser.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 StubAbortJobRequest());
PrintingAborted?.Invoke(this, new PrintingEventArgs(handler, originalJob));
PrintingEnded?.Invoke(this, new PrintingEventArgs(handler, originalJob));
handler.RaiseCanceled();
}
catch (Exception ex)
{
LogManager.Log(ex, "Failed to cancel job.");
}
}, originalJob, ticket, processParameters, JobHandlingMode);
handler.StatusChanged += (x, s) =>
{
RunningJobStatus = s;
};
LogRequestSent(request);
bool responseLogged = false;
SendContinuousRequest(request, null, TimeSpan.FromSeconds(2)).Subscribe((response) =>
{
handler.RaiseStatusReceived(response.Message.Status);
if (!responseLogged)
{
responseLogged = true;
Status = MachineStatuses.Printing;
RunningJob = originalJob;
PrintingStarted?.Invoke(this, new PrintingEventArgs(handler, originalJob));
LogResponseReceived(response.Message);
}
}, (ex) =>
{
if (!(ex is ContinuousResponseAbortedException))
{
Status = MachineStatuses.ReadyToDye;
if (!handler.IsCanceled)
{
PrintingFailed?.Invoke(this, new PrintingFailedEventArgs(handler, originalJob, ex));
PrintingEnded?.Invoke(this, new PrintingEventArgs(handler, originalJob));
handler.RaiseFailed(ex);
LogRequestFailed(request, ex);
}
}
else
{
Status = MachineStatuses.ReadyToDye;
}
}, () =>
{
Status = MachineStatuses.ReadyToDye;
PrintingCompleted?.Invoke(this, new PrintingEventArgs(handler, originalJob));
PrintingEnded?.Invoke(this, new PrintingEventArgs(handler, originalJob));
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)
{
try
{
hardwareVersion = configuration.GetHardwareConfiguration().Merge(hardwareVersion);
}
catch (Exception ex)
{
LogManager.Log(ex, "Error merging hardware configuration to hardware version.");
}
HardwareConfiguration hardwareConfiguration = new HardwareConfiguration();
foreach (var dancer in hardwareVersion.HardwareDancers.Where(x => x.Active))
{
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.Where(x => x.Active))
{
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.Where(x => x.Active))
{
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.Where(x => x.Active))
{
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.Where(x => x.Active))
{
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 blower in hardwareVersion.HardwareBlowers.Where(x => x.Active))
{
PMR.Hardware.HardwareBlower item = new PMR.Hardware.HardwareBlower();
blower.MapPrimitivesTo(item);
item.HardwareBlowerType = (PMR.Hardware.HardwareBlowerType)blower.HardwareBlowerType.Code;
hardwareConfiguration.Blowers.Add(item);
}
foreach (var breakSensor in hardwareVersion.HardwareBreakSensors.Where(x => x.Active))
{
PMR.Hardware.HardwareBreakSensor item = new PMR.Hardware.HardwareBreakSensor();
breakSensor.MapPrimitivesTo(item);
item.HardwareBreakSensorType = (PMR.Hardware.HardwareBreakSensorType)breakSensor.HardwareBreakSensorType.Code;
hardwareConfiguration.BreakSensors.Add(item);
}
foreach (var idsPack in configuration.NoneEmptyIdsPacks.OrderBy(x => x.PackIndex))
{
PMR.Hardware.HardwareDispenser item = new PMR.Hardware.HardwareDispenser();
item.Capacity = idsPack.Dispenser.DispenserType.Capacity;
item.HardwareDispenserType = (PMR.Hardware.HardwareDispenserType)idsPack.Dispenser.DispenserType.Code;
item.Index = idsPack.PackIndex;
item.NlPerPulse = idsPack.Dispenser.NlPerPulse;
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, null, TimeSpan.FromSeconds(5)).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, null, TimeSpan.FromSeconds(5)).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);
}
///
/// Sets the state of the specified heater type.
///
/// The heater.
/// Set point temperature.
///
public async Task SetHeaterState(HeaterType heater, double setPoint)
{
SetHeaterStateResponse response = null;
SetHeaterStateRequest request = new SetHeaterStateRequest()
{
HeaterType = heater,
SetPoint = setPoint,
IsActive = true,
};
try
{
LogRequestSent(request);
response = await SendRequest(request);
LogResponseReceived(response);
}
catch (Exception ex)
{
LogRequestFailed(request, ex);
throw ex;
}
return response;
}
///
/// Sets the state of the specified blower.
///
/// The blower.
/// Blower on/off.
/// The voltage in millivolts.
///
public async Task SetBlowerState(PMR.Hardware.HardwareBlowerType blower, bool isActive, double voltage)
{
SetBlowerStateResponse response = null;
SetBlowerStateRequest request = new SetBlowerStateRequest()
{
BlowerType = blower,
Voltage = voltage,
IsActive = isActive,
};
try
{
LogRequestSent(request);
response = await SendRequest(request);
LogResponseReceived(response);
}
catch (Exception ex)
{
LogRequestFailed(request, ex);
throw ex;
}
return response;
}
///
/// Sets the state of the specified valve type.
///
/// The valve.
/// Valve state.
///
public async Task SetValveState(ValveType valve, ValveStateCode state)
{
SetValveStateResponse response = null;
SetValveStateRequest request = new SetValveStateRequest()
{
ValveType = valve,
State = state,
};
try
{
LogRequestSent(request);
response = await SendRequest(request);
LogResponseReceived(response);
}
catch (Exception ex)
{
LogRequestFailed(request, ex);
throw ex;
}
return response;
}
///
/// 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);
}
///
/// Resets the embedded device.
///
///
public async Task Reset()
{
StubFpgaWriteRegResponse response = null;
StubFpgaWriteRegRequest request = null;
try
{
request = new StubFpgaWriteRegRequest()
{
Address = 0x60000800 | 0x3D0,
Value = 0x0
};
LogRequestSent(request);
response = await SendRequest(request);
LogResponseReceived(response);
}
catch (Exception ex)
{
LogRequestFailed(request, ex);
throw ex;
}
Thread.Sleep(1000);
try
{
request = new StubFpgaWriteRegRequest()
{
Address = 0x60000800 | 0x3D0,
Value = 0x1
};
LogRequestSent(request);
response = await SendRequest(request);
LogResponseReceived(response);
}
catch (Exception ex)
{
LogRequestFailed(request, ex);
throw ex;
}
return response;
}
///
/// Resets the device through the DFU channel.
///
///
public Task ResetDFU()
{
return Task.Factory.StartNew(() =>
{
LogManager.Log("Performing device reset through DFU...");
//LogManager.Log("Disconnecting Operator...");
//Disconnect().Wait();
//LogManager.Log("Operator disconnected.");
FirmwareUpdateManager updateManager = new FirmwareUpdateManager();
LogManager.Log("Initializing DFU API...");
updateManager.Initialize();
LogManager.Log("Enumerating DFU devices...");
var device = updateManager.GetAvailableDevices(false).Where(x => !x.DeviceName.Contains("In-Circuit Debug Interface")).FirstOrDefault();
if (device != null)
{
LogManager.Log($"DFU device found: '{device.DeviceName}'.");
LogManager.Log("Switching to DFU mode...");
device.SwitchToDFUMode();
Thread.Sleep(6000);
LogManager.Log("Reattaching to DFU device...");
device = updateManager.GetAvailableDevices(false).Where(x => !x.DeviceName.Contains("In-Circuit Debug Interface")).FirstOrDefault();
if (device != null)
{
LogManager.Log("Resetting device...");
device.Reset();
Thread.Sleep(1000);
LogManager.Log("Reset completed.");
}
else
{
throw LogManager.Log(new Exception("DFU device not found."));
}
}
else
{
throw LogManager.Log(new Exception("DFU device not found."));
}
});
}
///
/// Creates a storage manager for managing the machine file system.
///
///
public StorageManager CreateStorageManager()
{
return new StorageManager(this);
}
///
/// Upgrades the firmware.
///
/// The TFP stream (Tango Firmware Package File).
///
public async Task UpgradeFirmware(Stream tfpStream)
{
bool cancel = false;
ZipFile zip = null;
Action abortAction = null;
var upgradeHandler = new FirmwareUpgradeHandler(() =>
{
cancel = true;
abortAction?.Invoke();
});
try
{
if (Status != MachineStatuses.ReadyToDye)
{
throw LogManager.Log(new InvalidOperationException($"Could not perform firmware upgrade while operator status is '{Status}'."));
}
var package_info = await GetFirmwarePackageInfo(tfpStream);
tfpStream.Position = 0;
zip = ZipFile.Read(tfpStream);
var storage = CreateStorageManager();
var drive = await storage.GetStorageDrive();
var root = await storage.GetRootFolder();
var existing_folder = root.Items.SingleOrDefault(x => x.Name == FIRMWARE_UPGRADE_FOLDER_NAME);
if (existing_folder != null)
{
await storage.DeleteItem(existing_folder);
}
String package_folder = Path.Combine(drive.Root, FIRMWARE_UPGRADE_FOLDER_NAME);
await storage.CreateFolder(package_folder);
List handlers = new List();
List entries = zip.Entries.ToList();
List streams = new List();
var keepAlive = UseKeepAlive;
UseKeepAlive = false;
Action upgradeDFU = null;
Action uploadNext = null;
Action validate = null;
Action activate = null;
Action postActivation = null;
Status = MachineStatuses.Upgrading;
abortAction = new Action(() =>
{
Status = MachineStatuses.ReadyToDye;
});
upgradeDFU = new Action(() =>
{
try
{
if (FirmwareUpgradeMode.HasFlag(FirmwareUpgradeModes.DFU))
{
var mcuEntry = zip.Entries.Single(x => x.FileName == package_info.FileDescriptors.Single(y => y.Destination == VersionFileDestination.Mcu).FileName);
MemoryStream ms = new MemoryStream();
mcuEntry.Extract(ms);
ms.Position = 0;
byte[] data = ms.ToArray();
ms.Dispose();
FirmwareUpgradeManager upgradeManager = new FirmwareUpgradeManager();
upgradeManager.UpgradeProgress += (sender, e) =>
{
upgradeHandler.Total = (long)e.Total;
upgradeHandler.RaiseProgress((long)e.Progress, FirmwareUpgradeStatus.Upgrading, e.State.ToDescription());
};
Adapter.Disconnect().Wait();
if (MachineEventsStateProvider != null)
{
MachineEventsStateProvider.Reset();
}
upgradeManager.PerformUpgrade(data).Wait();
upgradeHandler.RaiseProgress(100, FirmwareUpgradeStatus.Upgrading, "Waiting for the device...");
Thread.Sleep(5000);
upgradeHandler.RaiseProgress(100, FirmwareUpgradeStatus.Upgrading, "Connecting...");
Adapter.Connect().Wait();
Connect().Wait();
upgradeHandler.RaiseProgress(100, FirmwareUpgradeStatus.Upgrading, "Connected.");
Thread.Sleep(2000);
upgradeHandler.RaiseProgress(100, FirmwareUpgradeStatus.Upgrading, "Waiting...");
Thread.Sleep(2000);
Status = MachineStatuses.Upgrading;
}
if (FirmwareUpgradeMode.HasFlag(FirmwareUpgradeModes.TFP_PACKAGE))
{
upgradeHandler.Total = zip.Entries.Sum(x => x.UncompressedSize);
uploadNext();
}
else
{
postActivation();
}
}
catch (Exception ex)
{
Status = MachineStatuses.ReadyToDye;
upgradeHandler.RaiseFailed(ex);
return;
}
});
uploadNext = new Action(() =>
{
if (entries.Count > 0)
{
try
{
var entry = entries.First();
entries.Remove(entry);
var reader = entry.OpenReader();
streams.Add(reader);
var handler = storage.UploadFile(Path.Combine(package_folder, entry.FileName), reader).Result;
handlers.Add(handler);
handler.Canceled += (_, __) => { upgradeHandler.RaiseCanceled(); cancel = true; abortAction(); };
handler.Completed += (_, __) => uploadNext();
handler.Failed += (_, failedEx) => { upgradeHandler.RaiseFailed(failedEx); cancel = true; abortAction(); };
handler.Progress += (_, e) =>
{
if (cancel)
{
handler.Cancel();
return;
}
upgradeHandler.RaiseProgress(upgradeHandler.Current + e.Delta, FirmwareUpgradeStatus.Uploading, $"Uploading '{entry.FileName}'...");
};
}
catch (Exception ex)
{
abortAction();
upgradeHandler.RaiseFailed(ex);
}
}
else
{
validate();
}
});
validate = new Action(() =>
{
try
{
streams.ForEach(x => x.Dispose());
upgradeHandler.RaiseProgress(upgradeHandler.Total, FirmwareUpgradeStatus.Validating, "Validating version...");
var validateRequest = new ValidateVersionRequest();
validateRequest.Path = package_folder;
var validateResponse = SendRequest(validateRequest, TimeSpan.FromSeconds(10)).Result;
activate();
}
catch (Exception ex)
{
upgradeHandler.RaiseFailed(ex);
}
});
activate = new Action(() =>
{
try
{
upgradeHandler.RaiseProgress(upgradeHandler.Total, FirmwareUpgradeStatus.Activating, "Activating version...");
var activateRequest = new ActivateVersionRequest();
activateRequest.Path = package_folder;
var activateResponse = SendRequest(activateRequest, TimeSpan.FromSeconds(10)).Result;
postActivation();
}
catch (Exception ex)
{
upgradeHandler.RaiseFailed(ex);
}
});
postActivation = new Action(() =>
{
upgradeHandler.RaiseCompleted();
Status = MachineStatuses.ReadyToDye;
UseKeepAlive = keepAlive;
});
ThreadFactory.StartNew(() =>
{
upgradeDFU();
});
return upgradeHandler;
}
catch (Exception)
{
if (zip != null)
{
zip.Dispose();
}
throw;
}
}
///
/// Validates the firmware package integrity.
///
/// The TFP (Tango Firmware Package File) stream.
///
public Task GetFirmwarePackageInfo(Stream tfpStream)
{
return Task.Factory.StartNew(() =>
{
using (ZipFile zip = ZipFile.Read(tfpStream))
{
var reader = zip.Entries.SingleOrDefault(x => x.FileName == FIRMWARE_UPGRADE_CONFIG_FILE_NAME).OpenReader();
var info = VersionPackageDescriptor.Parser.ParseFrom(reader);
reader.Close();
reader.Dispose();
return info;
}
});
}
///
/// Directs the embedded device to validate the last uploaded firmware package.
///
///
public async Task ValidateFirmwareVersion(String path)
{
var validateRequest = new ValidateVersionRequest();
validateRequest.Path = path;
await SendRequest(validateRequest, TimeSpan.FromSeconds(10));
}
///
/// Directs the embedded device to validate the last uploaded firmware package.
///
///
public async Task ActivateFirmwareVersion(String path)
{
var activateRequest = new ActivateVersionRequest();
activateRequest.Path = path;
await SendRequest(activateRequest, TimeSpan.FromSeconds(10));
}
#endregion
}
}