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;
using System.Globalization;
using Tango.PMR.Power;
using Tango.PMR.ThreadLoading;
using Tango.BL.DTO;
using Tango.PMR.IFS;
using System.Runtime.CompilerServices;
using Tango.BL.Interfaces;
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";
private String[] EUREKA_FIRMWARE_UPGRADE_DRIVE_LABELS = { "NOD_H743ZI2", "NOD_H753ZI2", "NOD_H753ZI", "STLINK_V3P" };
public const int MAX_DISPENSER_NANOLITER = 130000000;
public const double MAX_MIDTANK_LITERS = 1.8;
public const double EMPTY_MIDTANK_LITERS = 0.2;
public const double LOW_MIDTANK_LITERS = 0.3;
public const double OVERALL_TEMPERATURE_OK = 35;
public const double OVERALL_TEMPERATURE_WARNING = 35;
public const double OVERALL_TEMPERATURE_ERROR = 40;
private bool _diagnosticsSent;
private bool _eventsSent;
private bool _debugSent;
private bool _machineStatusSent;
private bool _inkFillingStatusSent;
private bool _telemetryWireSent;
private bool _notificationsSent;
private bool _threadLoadingSent;
private bool _isPowerDownRequestInProgress;
private bool _isHeadCleaningInProgress;
private DateTime _diagnosticsTime;
private Configuration _machineConfiguration;
private MachineStatus _lastMachineStatus;
protected class CurrentJobContext
{
private List _liquidQuantities;
private Func> _getLiquidQuantities;
public MachineStatus MachineStatusOnCreation { get; private set; }
public DateTime StartDate { get; set; }
public DateTime? UploadingStartDate { get; set; }
public DateTime? HeatingStartDate { get; set; }
public DateTime? ActualStartDate { get; set; }
public Job Job { get; set; }
public Job OriginalJob { get; set; }
public Job ClonedJob { get; set; }
public Job JobForJobRun { get; set; }
public JobTicket JobTicket { get; set; }
public JobHandler JobHandler { get; set; }
public bool RequestSent { get; set; }
public JobRequest JobRequest { get; set; }
public bool JobCompleted { get; set; }
public bool ResponseLogged { get; set; }
public bool InitialUseKeepAlive { get; set; }
public bool IsResumingJob { get; set; }
public List GetLiquidQuantities()
{
if (_liquidQuantities == null)
{
_liquidQuantities = _getLiquidQuantities(this).ToList();
}
return _liquidQuantities;
}
public CurrentJobContext(MachineStatus currentMachineStatus, Func> getLiquidQuantities)
{
MachineStatusOnCreation = currentMachineStatus.Clone();
_getLiquidQuantities = getLiquidQuantities;
JobRequest = new JobRequest();
StartDate = DateTime.UtcNow;
}
}
private List _emulatedEvents;
private List _bitResults;
private JobSpoolType _currentSpoolType;
private String _lastWasteReplaceRequestToken;
public static String EmbeddedLogsFolder { get; private set; }
public static String EmbeddedLogsTag { get; private set; }
public static SessionFileLogger SessionLogger { get; set; }
public static String CachedJobOperationFile { get; set; }
#region Classes
private class RequiredLiquid
{
public IdsPack IdsPack { get; set; }
public long Quantity { get; set; }
}
#endregion
#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);
}
if (SessionLogger == null)
{
SessionLogger = new SessionFileLogger();
LogManager.Default.RegisterLogger(SessionLogger);
}
CachedJobOperationFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Twine", "Tango", "Job Resume", Path.GetFileNameWithoutExtension(AppDomain.CurrentDomain.FriendlyName), "CachedJobOperation.cache");
}
///
/// Initializes a new instance of the class.
///
public MachineOperator() : base()
{
_bitResults = new List();
_emulatedEvents = new List();
ComponentName = $"Machine Operator {_component_counter++}";
DeviceInformation = new DeviceInformation();
MachineEventsStateProvider = new DefaultMachineEventsStateProvider();
JobRunsLogger = new BasicJobRunsLogger(this);
JobRunsLogger.Start();
EnableEventsNotification = true;
EnableMachineStatusUpdates = true;
EnableInkFillingStatus = true;
EnableJobResume = true;
LogEmbeddedDebuggingToFile = true;
FirmwareUpgradeMode = FirmwareUpgradeModes.DFU | FirmwareUpgradeModes.TFP_PACKAGE;
GradientGenerationConfiguration = new DefaultGradientGenerationConfiguration();
EmergencyNotificationProvider = new UsbEmergencyNotificationProvider("COM1");
EnableJobLiquidQuantityValidation = true;
FailsWithAdapter = true;
IsSpoolReplaced = true;
ContinuousRequestTimeout = TimeSpan.FromSeconds(2);
ResetInkFllingStatus();
}
///
/// 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;
///
/// 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;
///
/// Occurs when the machine was connected and device has reported IsAfterReset.
///
public event EventHandler FirmwareStarted;
///
/// Occurs when power down has started.
///
public event EventHandler PowerDownStarted;
///
/// Occurs when the thread loading status has changed.
///
public event EventHandler ThreadLoadingStatusChanged;
///
/// Occurs when a thread loading confirmation is required.
///
public event EventHandler ThreadLoadingConfirmationRequired;
///
/// Occurs when thread loading has completed.
///
public event EventHandler ThreadLoadingCompleted;
///
/// Occurs when thread loading has failed.
///
public event EventHandler ThreadLoadingFailed;
///
/// Occurs when the power up sequence has started.
///
public event EventHandler PowerUpStarted;
///
/// Occurs when the power up sequence progress has changed.
///
public event EventHandler PowerUpProgress;
///
/// Occurs when power up sequence has completed successfully.
///
public event EventHandler PowerUpCompleted;
///
/// Occurs when power up sequence has failed.
///
public event EventHandler PowerUpFailed;
///
/// Occurs when power up sequence has ended. Could be due to no response to the request!
///
public event EventHandler PowerUpEnded;
///
/// Occurs when a head cleaning job has ended.
///
public event EventHandler HeadCleaningEnded;
///
/// Occurs when the ink filling status has changed.
///
public event EventHandler InkFillingStatusChanged;
///
/// Occurs when waste replacement is required.
///
public event EventHandler WasteReplacementRequired;
public event EventHandler TelemetryWireAvailable;
public event EventHandler NotificationAvailable;
#endregion
#region Properties
///
/// Gets or sets a value indicating whether to create a new designated session log file each successful connection.
/// This log file will contain standard logs that have occurred between the last connection and disconnection states.
///
public static bool EnableSessionLogFile
{
get { return SessionLogger.Enabled; }
set
{
SessionLogger.Enabled = value;
}
}
///
/// 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; }
///
/// Gets or sets the way of calculating how much liquid was spent during the job.
///
public JobLiquidQuantityCalculationMode JobLiquidQuantityCalculationMode { 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));
RaisePropertyChanged(nameof(IsConnected));
}
}
}
///
/// Gets a value indicating whether the machine is connected and status is not disconnected.
///
public bool IsConnected
{
get { return State == TransportComponentState.Connected && Status != MachineStatuses.Disconnected; }
}
private MachineStatus _machineStatus;
///
/// Gets the machine embedded device status.
///
public MachineStatus MachineStatus
{
get { return _machineStatus; }
private set { _machineStatus = value; RaisePropertyChangedAuto(); }
}
private InkFillingStatus _inkFillingStatus;
///
/// Gets or sets the ink filling status.
///
public InkFillingStatus InkFillingStatus
{
get { return _inkFillingStatus; }
private set { _inkFillingStatus = value; RaisePropertyChangedAuto(); }
}
private StartThreadLoadingResponse _threadLoadingStatus;
///
/// Gets the current thread loading status.
///
public StartThreadLoadingResponse ThreadLoadingStatus
{
get { return _threadLoadingStatus; }
private set { _threadLoadingStatus = 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 || Status == MachineStatuses.PowerUp || Status == MachineStatuses.Standby;
}
}
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 _enableInkFillingStatus;
public bool EnableInkFillingStatus
{
get { return _enableInkFillingStatus; }
set
{
if (_enableInkFillingStatus != value)
{
_enableInkFillingStatus = value;
RaisePropertyChangedAuto();
OnEnableInkFillingStatus(value);
}
}
}
private bool _enableAutomaticThreadLoading;
///
/// Gets or sets a value indicating whether to enable automatic thread loading support.
///
public bool EnableAutomaticThreadLoading
{
get { return _enableAutomaticThreadLoading; }
set
{
_enableAutomaticThreadLoading = value;
RaisePropertyChangedAuto();
OnEnableAutomaticThreadLoadingChanged(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();
}
}
private bool _enablePowerUpSequence;
///
/// Gets or sets a value indicating whether to enable the power sequence tracking.
///
public bool EnablePowerUpSequence
{
get { return _enablePowerUpSequence; }
set { _enablePowerUpSequence = value; RaisePropertyChangedAuto(); }
}
private bool _enableTelemetryWire;
public bool EnableTelemetryWire
{
get { return _enableTelemetryWire; }
set
{
if (_enableTelemetryWire != value)
{
_enableTelemetryWire = value;
RaisePropertyChangedAuto();
OnEnableTelemetryWireChanged(value);
}
}
}
private bool _enableNotifications;
public bool EnableNotifications
{
get { return _enableNotifications; }
set
{
if (_enableNotifications != value)
{
_enableNotifications = value;
RaisePropertyChangedAuto();
OnEnableNotificationsChanged(value);
}
}
}
///
/// 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; }
///
/// Gets or sets the general continuous request timeout.
///
public TimeSpan ContinuousRequestTimeout { get; set; }
///
/// Gets a value indicating whether the spool was replaced after the last job.
///
public bool IsSpoolReplaced { get; private set; }
///
/// Gets or sets the type of the machine.
///
public MachineTypes MachineType { 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;
LogManager.Log($"Sending '{nameof(StartDiagnosticsRequest)}'...");
SendContinuousRequest(request, new TransportContinuousRequestConfig() { ShouldLog = false }).ObserveOn(new NewThreadScheduler()).Subscribe(
(response) =>
{
if (!responseLogged)
{
_diagnosticsTime = DateTime.Now;
responseLogged = true;
}
else
{
_diagnosticsTime = _diagnosticsTime.Add(TimeSpan.FromMilliseconds(response.Message.ElapsedMilli));
}
response.Message.DateTime = _diagnosticsTime.ToString("MM/dd/yyyy HH:mm:ss.fff");
OnDiagnosticsDataAvailable(response);
},
(ex) =>
{
_diagnosticsSent = false;
},
() =>
{
_diagnosticsSent = false;
LogManager.Log("Diagnostics response completed!?", LogCategory.Warning);
});
}
else if (_diagnosticsSent)
{
_diagnosticsSent = false;
if (State == TransportComponentState.Connected)
{
var req = new StopDiagnosticsRequest();
try
{
var res = await SendRequest(req, new TransportRequestConfig() { ShouldLog = true });
}
catch { }
}
}
}
private void OnEnableTelemetryWireChanged(bool value)
{
if (value && State == TransportComponentState.Connected && !_telemetryWireSent)
{
var request = new StartTelemetryWireRequest();
_telemetryWireSent = true;
LogManager.Log($"Sending '{nameof(StartTelemetryWireRequest)}'...");
SendContinuousRequest(request, new TransportContinuousRequestConfig() { ShouldLog = false }).ObserveOn(new NewThreadScheduler()).Subscribe(
(response) =>
{
OnTelemetryWireAvailable(response);
},
(ex) =>
{
_telemetryWireSent = false;
},
() =>
{
_telemetryWireSent = false;
LogManager.Log("Telemetry Wire response completed!?", LogCategory.Warning);
});
}
}
private void OnEnableNotificationsChanged(bool value)
{
if (value && State == TransportComponentState.Connected && !_notificationsSent)
{
var request = new StartNotificationRequest();
_notificationsSent = true;
LogManager.Log($"Sending '{nameof(StartNotificationRequest)}'...");
SendContinuousRequest(request, new TransportContinuousRequestConfig() { ShouldLog = false }).ObserveOn(new NewThreadScheduler()).Subscribe(
(response) =>
{
OnNotificationAvailable(response);
},
(ex) =>
{
_notificationsSent = false;
},
() =>
{
_notificationsSent = false;
LogManager.Log("Notifications response completed!?", LogCategory.Warning);
});
}
}
///
/// 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, new TransportContinuousRequestConfig() { ShouldLog = true }).ObserveOn(new NewThreadScheduler()).Subscribe(
(response) =>
{
OnEventsNotification(response);
if (!responseLogged)
{
responseLogged = true;
}
},
(ex) =>
{
_eventsSent = false;
},
() =>
{
_eventsSent = false;
LogManager.Log("Events Notification response completed!?", LogCategory.Warning);
});
}
else if (_eventsSent)
{
_eventsSent = false;
if (State == TransportComponentState.Connected)
{
var req = new StopEventsNotificationRequest();
try
{
var res = await SendRequest(req, new TransportRequestConfig() { ShouldLog = true });
}
catch { }
}
}
}
///
/// 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, new TransportContinuousRequestConfig() { ShouldLog = true }).ObserveOn(new NewThreadScheduler())
.Subscribe
(
(response) =>
{
if (!responseLogged)
{
responseLogged = true;
}
OnDebugLogAvailable(response);
},
(ex) =>
{
_debugSent = false;
},
() =>
{
_debugSent = false;
});
}
else if (_debugSent)
{
_debugSent = false;
if (State == TransportComponentState.Connected)
{
var req = new StopDebugLogRequest();
try
{
var res = await SendRequest(req, new TransportRequestConfig() { ShouldLog = true });
}
catch { }
}
}
}
///
/// 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, new TransportContinuousRequestConfig() { ShouldLog = true }).ObserveOn(new NewThreadScheduler()).Subscribe(
(response) =>
{
_lastMachineStatus = response.Message.Status?.Clone();
OnMachineStatusChanged(response);
if (!responseLogged)
{
responseLogged = true;
}
},
(ex) =>
{
_machineStatusSent = false;
},
() =>
{
_machineStatusSent = false;
LogManager.Log("Machine status update response completed!?", LogCategory.Warning);
});
}
else if (_machineStatusSent)
{
_machineStatusSent = false;
if (State == TransportComponentState.Connected)
{
var req = new StopMachineStatusUpdateRequest();
try
{
var res = await SendRequest(req, new TransportRequestConfig() { ShouldLog = true });
}
catch { }
}
}
}
///
/// Called when the enable ink filling status has been changed.
///
/// if set to true [value].
protected virtual void OnEnableInkFillingStatus(bool value)
{
if (value && State == TransportComponentState.Connected && !_inkFillingStatusSent)
{
var request = new StartInkFillingStatusRequest();
bool responseLogged = false;
_inkFillingStatusSent = true;
SendContinuousRequest(request, new TransportContinuousRequestConfig() { ShouldLog = true }).ObserveOn(new NewThreadScheduler()).Subscribe(
(response) =>
{
OnInkFillingStatusChanged(response);
if (!responseLogged)
{
responseLogged = true;
}
},
(ex) =>
{
_inkFillingStatusSent = false;
},
() =>
{
_inkFillingStatusSent = false;
LogManager.Log("Ink filling status response completed!?", LogCategory.Warning);
});
}
else if (_inkFillingStatusSent)
{
_inkFillingStatusSent = false;
}
}
///
/// Called when the enable automatic thread loading has been changed
///
/// if set to true [value].
protected virtual async void OnEnableAutomaticThreadLoadingChanged(bool value)
{
if (value && State == TransportComponentState.Connected && !_threadLoadingSent)
{
var request = new StartThreadLoadingRequest();
bool responseLogged = false;
_threadLoadingSent = true;
SendContinuousRequest(request, new TransportContinuousRequestConfig() { ShouldLog = true }).ObserveOn(new NewThreadScheduler()).Subscribe(
(response) =>
{
OnThreadLoadingStatusChanged(response);
if (!responseLogged)
{
responseLogged = true;
}
},
(ex) =>
{
_threadLoadingSent = false;
},
() =>
{
_threadLoadingSent = false;
LogManager.Log("Thread loading response completed!?", LogCategory.Warning);
});
}
else if (_threadLoadingSent)
{
_threadLoadingSent = false;
if (State == TransportComponentState.Connected)
{
var req = new StopThreadLoadingRequest();
try
{
var res = await SendRequest(req, new TransportRequestConfig() { ShouldLog = true });
}
catch { }
}
}
}
///
/// Invokes the event.
///
/// The sensors data.
protected virtual void OnDiagnosticsDataAvailable(StartDiagnosticsResponse data)
{
DiagnosticsDataAvailable?.Invoke(this, data);
}
private void OnTelemetryWireAvailable(StartTelemetryWireResponse response)
{
TelemetryWireAvailable?.Invoke(this, response);
}
private void OnNotificationAvailable(StartNotificationResponse response)
{
NotificationAvailable?.Invoke(this, response);
}
///
/// Called when events notification message has been received.
///
/// The response.
protected virtual void OnEventsNotification(StartEventsNotificationResponse response)
{
if (MachineEventsStateProvider != null)
{
var events = response.Events;
foreach (var emulated in _emulatedEvents)
{
if (!events.Any(x => x.Type == emulated.Type))
{
events.Add(emulated);
}
}
MachineEventsStateProvider.ApplyEvents(events);
}
EventsNotification?.Invoke(this, response);
}
///
/// 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 machine status has been updated.
///
/// The response.
protected virtual void OnMachineStatusChanged(StartMachineStatusUpdateResponse response)
{
if (response.Status == null) return;
bool changed = (MachineStatus == null || response.Status.State != MachineStatus.State);
foreach (var idsPack in response.Status.IDSPacksLevels)
{
if (idsPack.DispenserLevel > 0 && idsPack.DispenserLevel64 == 0)
{
idsPack.DispenserLevel64 = idsPack.DispenserLevel;
}
}
MachineStatus = response.Status;
MachineStatusChanged?.Invoke(this, MachineStatus);
if (changed)
{
OnMachineStateChanged(MachineStatus.State);
}
if (MachineStatus.SpoolState == SpoolState.Absent)
{
IsSpoolReplaced = true;
}
}
///
/// Called when ink filling status has been changed.
///
/// The response.
protected virtual void OnInkFillingStatusChanged(StartInkFillingStatusResponse response)
{
if (response.Status == null || response.Status.CartridgesStatuses == null || response.Status.CartridgesStatuses.Count == 0) return;
int index = -1;
bool raiseChange = false;
foreach (var remoteCartridge in response.Status.CartridgesStatuses)
{
index++;
if (remoteCartridge.Cartridge == null)
{
LogManager.Log($"Remote cartridge arrived with null cartridge at position [{index}] and will be ignored.", LogCategory.Error);
continue;
}
var localCartridge = InkFillingStatus.CartridgesStatuses.SingleOrDefault(x => x.Cartridge.Index == remoteCartridge.Cartridge.Index && x.Cartridge.Slot == remoteCartridge.Cartridge.Slot);
if (localCartridge != null)
{
if (localCartridge.State != remoteCartridge.State)
{
localCartridge.State = remoteCartridge.State;
LogManager.Log($"{localCartridge.Cartridge.Slot} Cartridge '{localCartridge.Cartridge.Index}' state changed: '{localCartridge.State}' => '{remoteCartridge.State}'.");
}
if (remoteCartridge.Cartridge.Tag != null)
{
LogManager.Log($"{localCartridge.Cartridge.Slot} Cartridge '{localCartridge.Cartridge.Index}' Tag arrived:\n{remoteCartridge.Cartridge.Tag.ToJsonString()}");
}
localCartridge.Message = remoteCartridge.Message;
localCartridge.ProgressPercentage = remoteCartridge.ProgressPercentage;
raiseChange = true;
}
else
{
LogManager.Log($"Could not locate local cartridge with slot '{remoteCartridge.Cartridge.Slot}' and index '{remoteCartridge.Cartridge.Index}'.", LogCategory.Error);
}
}
if (raiseChange)
{
RaisePropertyChanged(nameof(InkFillingStatus));
InkFillingStatusChanged?.Invoke(this, new InkFillingStatusChangedEventArgs() { Status = InkFillingStatus });
}
}
///
/// Called when the machine state has been changed.
///
/// The state.
protected async virtual void OnMachineStateChanged(MachineState state)
{
LogManager.Log($"Machine State Changed: {state}.");
if (IsPrinting)
{
LogManager.Log($"Machine state change will not affect the machine operator status as it is now in a '{Status}' status.", LogCategory.Warning);
return;
}
switch (state)
{
case MachineState.PowerUp:
UpdateStatus(MachineStatuses.PowerUp);
break;
//case MachineState.PreparingJob:
// Status = MachineStatuses.GettingReady;
// break;
case MachineState.Ready:
UpdateStatus(MachineStatuses.ReadyToDye);
break;
case MachineState.Sleep:
UpdateStatus(MachineStatuses.Standby);
break;
case MachineState.PowerDown:
UpdateStatus(MachineStatuses.PowerDown);
break;
case MachineState.ShuttingDown:
UpdateStatus(MachineStatuses.ShuttingDown);
if (!_isPowerDownRequestInProgress)
{
try
{
await PowerDown();
}
catch { }
}
break;
case MachineState.Error:
//Status = MachineStatuses.Error;
break;
}
}
///
/// Called when the thread loading status has been changed.
///
/// The response.
protected virtual void OnThreadLoadingStatusChanged(StartThreadLoadingResponse response)
{
bool changed = (ThreadLoadingStatus == null || response.State != ThreadLoadingStatus.State || response.ErrorReason != ThreadLoadingStatus.ErrorReason);
if (changed)
{
ThreadLoadingStatus = response;
ThreadLoadingStatusChanged?.Invoke(this, response);
LogManager.Log($"Thread Loading Status Changed: {ThreadLoadingStatus.State}.");
switch (ThreadLoadingStatus.State)
{
case ThreadLoadingState.ReadyForLoading:
LogManager.Log("Thread loading is ready for loading. Invoking confirmation event...");
ThreadLoadingConfirmationRequired?.Invoke(this, new ThreadLoadingConfirmationRequiredEventArgs((processTable) =>
{
//Confirm Action
try
{
var process = processTable.ToProcessParametersPMR();
LogManager.Log($"Thread loading confirmation received with process parameters:\n{process.ToJsonString()}");
LogManager.Log("Sending continue thread loading request...");
var r = SendRequest(new ContinueThreadLoadingRequest()
{
ProcessParameters = process,
}, new TransportRequestConfig() { ShouldLog = true }).Result;
}
catch (Exception ex)
{
LogManager.Log(ex, "Error confirming thread loading sequence.");
}
})
{
Status = ThreadLoadingStatus,
});
break;
case ThreadLoadingState.Completed:
ThreadLoadingCompleted?.Invoke(this, ThreadLoadingStatus);
break;
case ThreadLoadingState.FinalizationError:
case ThreadLoadingState.PreparationError:
ThreadLoadingFailed?.Invoke(this, ThreadLoadingStatus);
break;
}
}
}
///
/// Called when a new request has been received.
///
/// The request.
protected override void OnRequestReceived(RequestReceivedEventArgs e)
{
base.OnRequestReceived(e);
if (e.Handled) return;
var container = e.Container;
if (container.Type == MessageType.CartridgeValidationRequest)
{
e.Handled = true;
OnCartridgeValidationRequestReceived(container.Token, MessageFactory.ExtractMessageFromContainer(container));
}
else if (container.Type == MessageType.UpdateStatusRequest)
{
e.Handled = true;
OnUpdateStatusRequestReceived(container.Token, MessageFactory.ExtractMessageFromContainer(container));
}
else if (container.Type == MessageType.WasteReplaceRequest)
{
e.Handled = true;
OnWasteReplacementRequired(container.Token, MessageFactory.ExtractMessageFromContainer(container));
}
}
///
/// 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
{
UpdateStatus((MachineStatuses)request.Status);
}
catch (Exception ex)
{
LogManager.Log(ex);
}
try
{
SendResponse(new UpdateStatusResponse(), token);
}
catch (Exception ex)
{
LogManager.Log(ex, "Error sending UpdateStatus response.");
}
}
///
/// Called when the printing has been started.
///
/// The handler.
/// The job.
protected virtual void OnPrintingStarted(CurrentJobContext context)
{
LogManager.Log("Raising printing started event...");
PrintingStarted?.Invoke(this, new PrintingEventArgs(context.JobHandler, context.JobForJobRun)
{
StartDate = context.StartDate,
IsResumingJob = context.IsResumingJob
});
}
///
/// Called when the printing has been completed.
///
/// The handler.
/// The job.
protected virtual void OnPrintingCompleted(CurrentJobContext context)
{
LogManager.Log("Raising printing completed event...");
PrintingCompleted?.Invoke(this, new PrintingEventArgs(context.JobHandler, context.JobForJobRun)
{
LiquidQuantities = context.GetLiquidQuantities(),
StartDate = context.StartDate,
UploadingStartTime = context.UploadingStartDate,
HeatingStartTime = context.HeatingStartDate,
ActualStartTime = context.ActualStartDate,
IsResumingJob = context.IsResumingJob
});
OnPrintingEnded(context);
}
///
/// Called when the printing has been failed.
///
/// The handler.
/// The job.
/// The exception.
protected virtual void OnPrintingFailed(CurrentJobContext context, Exception exception)
{
LogManager.Log("Raising printing failed event...");
PrintingFailed?.Invoke(this, new PrintingFailedEventArgs(context.JobHandler, context.JobForJobRun, exception)
{
LiquidQuantities = context.GetLiquidQuantities(),
StartDate = context.StartDate,
UploadingStartTime = context.UploadingStartDate,
HeatingStartTime = context.HeatingStartDate,
ActualStartTime = context.ActualStartDate,
IsResumingJob = context.IsResumingJob
});
OnPrintingEnded(context);
}
///
/// Called when the printing has been aborted.
///
/// The handler.
/// The job.
protected virtual void OnPrintingAborted(CurrentJobContext context)
{
LogManager.Log("Raising printing aborted event...");
PrintingAborted?.Invoke(this, new PrintingEventArgs(context.JobHandler, context.JobForJobRun)
{
LiquidQuantities = context.GetLiquidQuantities(),
StartDate = context.StartDate,
UploadingStartTime = context.UploadingStartDate,
HeatingStartTime = context.HeatingStartDate,
ActualStartTime = context.ActualStartDate,
IsResumingJob = context.IsResumingJob
});
OnPrintingEnded(context);
}
///
/// Called when the printing has been ended.
///
/// The handler.
/// The job.
protected virtual void OnPrintingEnded(CurrentJobContext context)
{
IsSpoolReplaced = false;
LogManager.Log("Raising printing ended event...");
PrintingEnded?.Invoke(this, new PrintingEventArgs(context.JobHandler, context.JobForJobRun)
{
LiquidQuantities = context.GetLiquidQuantities(),
StartDate = context.StartDate,
UploadingStartTime = context.UploadingStartDate,
HeatingStartTime = context.HeatingStartDate,
ActualStartTime = context.ActualStartDate,
IsResumingJob = context.IsResumingJob
});
}
protected virtual void OnHeadCleaningEnded(CurrentJobContext context, HeadCleaningHandler handler, JobRunStatus status)
{
HeadCleaningEnded?.Invoke(this, new HeadCleaningEndedEventArgs()
{
StartDate = context.StartDate,
Length = handler.Status.Total,
EndPosition = handler.Status.Progress,
Status = status,
LiquidQuantities = context.GetLiquidQuantities(),
});
}
protected virtual void OnWasteReplacementRequired(string token, WasteReplaceRequest wasteReplaceRequest)
{
if (_lastWasteReplaceRequestToken == null)
{
_lastWasteReplaceRequestToken = token;
WasteReplacementRequired?.Invoke(this, new EventArgs());
}
}
#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;
_telemetryWireSent = false;
_notificationsSent = false;
//MachineStatus = null;
if (Status != MachineStatuses.Disconnected)
{
UpdateStatus(MachineStatuses.Disconnected);
ResetEvents();
ResetInkFllingStatus();
}
}
}
private void ResetEvents()
{
if (MachineEventsStateProvider != null)
{
LogManager.Log("Resetting active events...");
_emulatedEvents.Clear();
MachineEventsStateProvider.Reset();
}
}
///
/// Disconnects the machine operator and the underlying transporter.
///
///
public async override Task Disconnect()
{
if (Status == MachineStatuses.Upgrading) return;
UpdateStatus(MachineStatuses.Disconnected);
if (MachineStatus != null)
{
MachineStatus.State = MachineState.Ready;
}
SessionLogger.EndSession();
if (State == TransportComponentState.Connected)
{
DisconnectRequest request = new DisconnectRequest();
try
{
var response = await SendRequest(request, new TransportRequestConfig() { ShouldLog = true });
UpdateStatus(MachineStatuses.Disconnected);
}
catch { }
}
ResetEvents();
ResetInkFllingStatus();
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(),
SpoolType = _currentSpoolType,
MachineType = (PMR.Common.MachineType)MachineType,
Supports64BitDispenserLevel = true
};
try
{
var response = await SendRequest(request, new TransportRequestConfig() { ShouldLog = true });
SessionLogger.CreateSession();
_isPowerDownRequestInProgress = false;
DeviceInformation = response.Message.DeviceInformation;
if (Status != MachineStatuses.Upgrading)
{
UpdateStatus(MachineStatuses.ReadyToDye);
}
_diagnosticsSent = false;
_eventsSent = false;
_debugSent = false;
_machineStatusSent = false;
_telemetryWireSent = false;
_notificationsSent = false;
_bitResults = null;
OnEnableDiagnosticsChanged(EnableDiagnostics);
OnEnableEmbeddedDebuggingChanged(EnableEmbeddedDebugging);
OnEnableEventsNotification(EnableEventsNotification);
OnEnableMachineStatusUpdatesChanged(EnableMachineStatusUpdates);
OnEnableAutomaticThreadLoadingChanged(EnableAutomaticThreadLoading);
OnEnableInkFillingStatus(EnableInkFillingStatus);
OnEnableTelemetryWireChanged(EnableTelemetryWire);
OnEnableNotificationsChanged(EnableNotifications);
if (EnablePowerUpSequence)
{
TrackPowerUpSequence();
}
if (EnableJobResume)
{
ResumeJob();
}
if (response.Message.IsAfterReset)
{
FirmwareStarted?.Invoke(this, new EventArgs());
}
}
catch (Exception ex)
{
SessionLogger.EndSession();
await base.Disconnect();
throw ex;
}
finally
{
UseKeepAlive = keep_alive;
}
}
}
#endregion
#region Private Methods
private void UpdateStatus(MachineStatuses status, [CallerMemberName] string caller = null, [CallerFilePath] string file = null, [CallerLineNumber] int lineNumber = 0)
{
if (Status != status)
{
Status = status;
LogManager.Log($"Machine operator status changed: {status}", caller, file, lineNumber);
}
}
private void ResetInkFllingStatus()
{
if (InkFillingStatus == null)
{
var status = new InkFillingStatus();
for (int i = 0; i < 8; i++)
{
status.CartridgesStatuses.Add(new CartridgeStatus()
{
Cartridge = new Cartridge()
{
Index = i,
Slot = CartridgeSlot.Ink,
},
State = CartridgeState.Absent
});
}
status.CartridgesStatuses.Add(new CartridgeStatus()
{
Cartridge = new Cartridge() { Index = 0, Slot = CartridgeSlot.WasteMiddle },
State = CartridgeState.Absent
});
status.CartridgesStatuses.Add(new CartridgeStatus()
{
Cartridge = new Cartridge() { Index = 1, Slot = CartridgeSlot.WasteLower },
State = CartridgeState.Absent
});
InkFillingStatus = status;
}
else
{
foreach (var cartridge in InkFillingStatus.CartridgesStatuses)
{
cartridge.ProgressPercentage = 0;
cartridge.Message = String.Empty;
cartridge.State = CartridgeState.Absent;
}
}
InkFillingStatusChanged?.Invoke(this, new InkFillingStatusChangedEventArgs() { Status = InkFillingStatus });
}
private void SaveCachedJobOperation(Job job, AdditionalJobConfiguration config)
{
try
{
LogManager.Log("Caching current job operation...");
CachedJobOperation cache = new CachedJobOperation();
cache.JobDTO = JobDTO.FromObservable(job);
cache.MachineStatus = MachineStatus;
cache.ProcessParametersDTO = ProcessParametersTableDTO.FromObservable(CurrentProcessParameters);
cache.MachineConfigurationDTO = ConfigurationDTO.FromObservable(job.Machine.Configuration);
cache.Config = config;
var json = JsonConvert.SerializeObject(cache);
Directory.CreateDirectory(Path.GetDirectoryName(CachedJobOperationFile));
File.WriteAllText(CachedJobOperationFile, json);
}
catch (Exception ex)
{
LogManager.Log(ex, "Error caching job operation for job resume.");
}
}
private CachedJobOperation LoadCachedJobOperation()
{
try
{
LogManager.Log("Loading last cached job operation...");
String json = File.ReadAllText(CachedJobOperationFile);
CachedJobOperation cache = JsonConvert.DeserializeObject(json);
return cache;
}
catch (Exception ex)
{
LogManager.Log(ex, "Error loading cache of last job operation for job resume.");
return null;
}
}
private void TrackPowerUpSequence()
{
LogManager.Log("Starting power up sequence tracking...");
bool started = false;
bool completed = false;
PowerUpState lastState = PowerUpState.None;
SendContinuousRequest(new StartPowerUpRequest(), new TransportContinuousRequestConfig()
{
ShouldLog = true,
Timeout = TimeSpan.FromSeconds(5)
}).Subscribe((response) =>
{
if (!started)
{
started = true;
PowerUpStarted?.Invoke(this, response);
}
PowerUpProgress?.Invoke(this, response);
var state = response.Message.State;
if (state != lastState)
{
LogManager.Log($"Power up sequence state changed to '{state}'...");
switch (state)
{
case PowerUpState.Error:
completed = true;
LogManager.Log($"Power up sequence failed with state '{state}'. ({response.Message.Message})");
PowerUpFailed?.Invoke(this, response);
PowerUpEnded?.Invoke(this, new EventArgs());
break;
case PowerUpState.Cancelled:
completed = true;
LogManager.Log($"Power up sequence canceled with state '{state}'. ({response.Message.Message})");
PowerUpEnded?.Invoke(this, new EventArgs());
break;
case PowerUpState.MachineReadyToDye:
completed = true;
LogManager.Log($"Power up sequence completed successfully with state '{state}'. ({response.Message.Message})");
PowerUpCompleted?.Invoke(this, response);
PowerUpEnded?.Invoke(this, new EventArgs());
break;
}
lastState = state;
}
}, (ex) =>
{
if (!completed)
{
completed = true;
LogManager.Log(ex, "Power up sequence tracking failed.");
PowerUpEnded?.Invoke(this, new EventArgs());
}
}, () =>
{
if (!completed)
{
completed = true;
PowerUpEnded?.Invoke(this, new EventArgs());
}
});
}
//private async void ResumeJob()
//{
// LogManager.Log("Checking if a job is in progress...");
// try
// {
// var res = await SendRequest(new CurrentJobRequest(), new TransportRequestConfig() { ShouldLog = true });
// if (res.Message.IsJobInProgress)
// {
// LogManager.Log("Job is in progress. Trying to resume job...");
// CachedJobOperation cache = LoadCachedJobOperation();
// if (cache == null)
// {
// LogManager.Log("Cannot resume current job with no cached operation.", LogCategory.Error);
// return;
// }
// Job job = null;
// Configuration configuration = null;
// ProcessParametersTable processParameters = null;
// try
// {
// processParameters = cache.ProcessParametersDTO.ToObservable();
// job = cache.JobDTO.ToObservable();
// configuration = cache.MachineConfigurationDTO.ToObservable();
// CurrentProcessParameters = processParameters;
// }
// catch (Exception ex)
// {
// LogManager.Log(ex, "Error deserializing cache job operation. Aborting resume.");
// return;
// }
// JobTicket jobTicket = res.Message.JobTicket;
// ResumingJobEventArgs args = new ResumingJobEventArgs(() =>
// {
// RunningJob = null;
// RunningJobStatus = null;
// var request = new ResumeCurrentJobRequest();
// JobHandler handler = null;
// handler = new JobHandler(async () =>
// {
// try
// {
// if (handler.CanCancel)
// {
// handler.CanCancel = false;
// handler.IsCanceled = true;
// LogManager.Log("Aborting current job...");
// var result = await SendRequest(new AbortJobRequest(), new TransportRequestConfig() { ShouldLog = true });
// SaveLastJobLiquidQuantities(job, configuration, processParameters, handler);
// OnPrintingAborted(handler, job);
// handler.RaiseCanceled();
// if (Status != MachineStatuses.Disconnected)
// {
// UpdateStatus(MachineStatuses.ReadyToDye);
// }
// }
// }
// catch (Exception ex)
// {
// handler.CanCancel = true;
// LogManager.Log(ex, "Failed to cancel job.");
// }
// }, job, jobTicket, processParameters, JobHandlingMode);
// handler.StatusChanged += (x, s) =>
// {
// RunningJobStatus = s;
// };
// if (MachineStatus != null)
// {
// _machineStatusBeforeJobStart = MachineStatus.Clone();
// }
// else
// {
// _machineStatusBeforeJobStart = cache.MachineStatus.Clone();
// }
// _jobStartDate = DateTime.UtcNow;
// _jobUploadingStartDate = _jobStartDate;
// _jobHeatingStartDate = _jobStartDate;
// _jobActualStartDate = null;
// bool responseLogged = false;
// bool completed = false;
// Thread.Sleep(500); //Just wait maybe Shlomo is getting this message to fast after restart ?
// SendContinuousRequest(request, new TransportContinuousRequestConfig() { ContinuousTimeout = TimeSpan.FromSeconds(10), ShouldLog = true }).Subscribe((response) =>
// {
// if (!completed)
// {
// handler.RaiseStatusReceived(response.Message.Status);
// _last_job_status = handler.Status;
// if (response.Message.Status.Progress > 0)
// {
// if (_jobActualStartDate == null)
// {
// _jobActualStartDate = DateTime.UtcNow;
// }
// }
// if (!responseLogged)
// {
// UpdateStatus(MachineStatuses.GettingReady);
// responseLogged = true;
// RunningJob = job;
// OnPrintingStarted(handler, job, true);
// }
// if (JobHandlingMode == JobHandlerModes.SettingUp)
// {
// if (response.Message.Status.Progress > CurrentProcessParameters.DryerBufferLengthMeters)
// {
// if (!completed)
// {
// UpdateStatus(MachineStatuses.Printing);
// }
// }
// }
// else
// {
// if (response.Message.Status.Progress > 0)
// {
// if (!completed)
// {
// UpdateStatus(MachineStatuses.Printing);
// }
// }
// }
// }
// }, (ex) =>
// {
// if (!completed)
// {
// completed = true;
// if (Status != MachineStatuses.Disconnected)
// {
// UpdateStatus(MachineStatuses.ReadyToDye);
// }
// if (!handler.IsCanceled)
// {
// SaveLastJobLiquidQuantities(job, configuration, processParameters, handler);
// Exception finalException = ex;
// if (ex is ContinuousResponseAbortedException continuousException)
// {
// finalException = new ContinuousResponseAbortedException($"Job aborted by the embedded device ({continuousException.Container.ErrorMessage}).");
// }
// OnPrintingFailed(handler, job, finalException);
// handler.RaiseFailed(finalException);
// }
// }
// }, () =>
// {
// if (!completed)
// {
// completed = true;
// UpdateStatus(MachineStatuses.ReadyToDye);
// SaveLastJobLiquidQuantities(job, configuration, processParameters, handler);
// OnPrintingCompleted(handler, job);
// handler.RaiseCompleted();
// }
// });
// return handler;
// });
// args.JobGuid = jobTicket.Guid;
// ResumingJob?.Invoke(this, args);
// }
// }
// catch (Exception ex)
// {
// LogManager.Log(ex);
// }
//}
///
/// 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...");
try
{
stops = GradientGenerationConfiguration.Generate(segment, job, processParameters, (e) =>
{
PreparingJobProgress?.Invoke(this, e);
});
}
catch (Exception ex)
{
throw new InvalidOperationException($"Error occurred while trying to generate a gradient.\n{ex.Message}");
}
LogManager.Log($"Gradient generated.");
PreparingJobProgress?.Invoke(this, new PreparingJobProgressEventArgs()
{
Job = job,
Total = job.Segments.Sum(x => x.Length),
Progress = job.Segments.Sum(x => x.Length),
});
}
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;
// bool responseLogged = false;
// var previous_segments_length = job.Segments.Where(x => x.SegmentIndex < segment.SegmentIndex).Sum(x => x.Length);
// SendContinuousRequest(request, new TransportContinuousRequestConfig() { ContinuousTimeout = ContinuousRequestTimeout, ShouldLog = true }).Subscribe((response) =>
// {
// response.Message.Status.Progress += previous_segments_length;
// handler.RaiseStatusReceived(response.Message.Status);
// if (!responseLogged && segment == job.OrderedSegments.First())
// {
// responseLogged = true;
// UpdateStatus(MachineStatuses.Printing);
// RunningJob = handler.Job;
// OnPrintingStarted(handler, handler.Job);
// }
// }, (ex) =>
// {
// if (!(ex is ContinuousResponseAbortedException))
// {
// UpdateStatus(MachineStatuses.ReadyToDye);
// if (!handler.IsCanceled)
// {
// OnPrintingFailed(handler, handler.Job, ex);
// handler.RaiseFailed(ex);
// }
// }
// else
// {
// UpdateStatus(MachineStatuses.ReadyToDye);
// }
// }, () =>
// {
// if (segment == job.OrderedSegments.Last())
// {
// UpdateStatus(MachineStatuses.ReadyToDye);
// OnPrintingCompleted(handler, handler.Job);
// handler.RaiseCompleted();
// }
// else
// {
// handler.RaiseSpoolChangeRequired(() =>
// {
// ContinueSingleSpoolJob(segment.GetNextSegment(), job, processParameters, handler);
// }, () =>
// {
// OnPrintingAborted(handler, handler.Job);
// UpdateStatus(MachineStatuses.ReadyToDye);
// handler.RaiseCanceled();
// });
// }
// });
//}
private List ValidateJobLiquidQuantity(Job job, ProcessParametersTable processParameters, Configuration configuration)
{
LogManager.Log("Validating job liquid quantities using integral...");
Dictionary liquidQuantities = new Dictionary();
List requiredLiquids = new List();
foreach (var pack in configuration.NoneEmptyIdsPacks.OrderBy(x => x.PackIndex))
{
liquidQuantities.Add(pack.PackIndex, 0);
}
int resolution = GradientGenerationConfiguration.ResolutionCM;
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;
List orderedBrushCollection = segment.BrushStops.OrderBy(x => x.OffsetMeters).ToList();
int solid_gradient_oeff = orderedBrushCollection.Count == 1 ? 1 : 2;
double prev_offset_cm = 0;
for (int brushIndex = 0; brushIndex < orderedBrushCollection.Count; brushIndex++)
{
var brush = orderedBrushCollection[brushIndex];
double brush_length_centimeters = 0d;
double brush_offset_cm = 0;
if ((brushIndex + 1) < orderedBrushCollection.Count)
{
brush_offset_cm = (brush.OffsetMeters * 100d);
double next_brush_offset_cm = (orderedBrushCollection[brushIndex + 1].OffsetMeters * 100d);
brush_length_centimeters = ((next_brush_offset_cm - brush_offset_cm) + (brush_offset_cm - prev_offset_cm));
if (brushIndex == 0)
{
// add a resolution step for first brush
brush_length_centimeters += resolution;
}
}
else//last brush or solid brush
{
brush_length_centimeters = (segment_length_cm - prev_offset_cm);
if (orderedBrushCollection.Count > 1)
{
// add a resolution for last brush , not solid brush
brush_length_centimeters -= resolution;
}
}
prev_offset_cm = brush_offset_cm;
foreach (var liquidVolumes in brush.LiquidVolumes)
{
liquidQuantities[liquidVolumes.IdsPack.PackIndex] += liquidVolumes.NanoliterPerCentimeter * (brush_length_centimeters / solid_gradient_oeff);
}
}
}
}
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.DispenserLevel64,
Required = (int)liquidQuantities[index],
Maximum = MAX_DISPENSER_NANOLITER,
};
requiredLiquids.Add(new RequiredLiquid()
{
IdsPack = idsPack,
Quantity = (int)liquidQuantities[index]
});
LogManager.Log($"Required {idsLevel.IdsPack.LiquidType.Type}: {idsLevel.Required}, Current: {idsLevel.Current}");
if (idsLevel.Required > idsLevel.Current)
{
shouldThrow = true;
string display_value = (((double)(idsLevel.Required - idsLevel.Current) / 1000000)).ToString("N2", CultureInfo.InvariantCulture);
idsLevel.Message = $"Missing {display_value} CC to complete the job.";
if (idsLevel.Required > idsLevel.Maximum)
{
display_value = (((double)(idsLevel.Required - idsLevel.Maximum)) / 1000000).ToString("N2", CultureInfo.InvariantCulture);
idsLevel.Message = $"Required ink exceeds the maximum capacity of the dispenser by {display_value} CC. Please reduce the segment length.";
}
}
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)
{
LogManager.Log("Liquid quantity validation failed due to insufficient quantity. Throwing exception...");
exception.IdsPackLevels = exception.IdsPackLevels.OrderBy(x => x.IdsPack.PackIndex).ToList();
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);
}
return requiredLiquids;
}
private List ValidateJobLiquidQuantity(JobTicket ticket, ProcessParametersTable processParameters, Configuration configuration)
{
LogManager.Log("Validating job liquid quantity using job ticket...");
Dictionary liquidQuantities = new Dictionary();
List requiredLiquids = new List();
foreach (var pack in configuration.NoneEmptyIdsPacks.OrderBy(x => x.PackIndex))
{
liquidQuantities.Add(pack.PackIndex, 0);
}
for (int segmentIndex = 0; segmentIndex < ticket.Segments.Count; segmentIndex++)
{
var segment = ticket.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 dispenser in stop.Dispensers)
{
liquidQuantities[dispenser.Index] += dispenser.NanoliterPerCentimeter * stop_length_centimeters;
}
}
}
foreach (var key in liquidQuantities.Select(x => x.Key).ToList())
{
liquidQuantities[key] *= Math.Max(ticket.NumberOfUnits, 1);
}
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.DispenserLevel64,
Required = (int)liquidQuantities[index],
Maximum = MAX_DISPENSER_NANOLITER,
};
requiredLiquids.Add(new RequiredLiquid()
{
IdsPack = idsPack,
Quantity = (int)liquidQuantities[index]
});
LogManager.Log($"Required {idsLevel.IdsPack.LiquidType.Type}: {idsLevel.Required}, Current: {idsLevel.Current}");
if (idsLevel.Required > idsLevel.Current)
{
shouldThrow = true;
string display_value = (((double)(idsLevel.Required - idsLevel.Current) / 1000000)).ToString("N2", CultureInfo.InvariantCulture);
idsLevel.Message = $"Missing {display_value} CC to complete the job.";
if (idsLevel.Required > idsLevel.Maximum)
{
display_value = (((double)(idsLevel.Required - idsLevel.Maximum)) / 1000000).ToString("N2", CultureInfo.InvariantCulture);
idsLevel.Message = $"Required ink exceeds the maximum capacity of the dispenser by {display_value} CC. Please reduce the segment length.";
}
}
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)
{
LogManager.Log("Liquid quantity validation failed due to insufficient quantity. Throwing exception...");
exception.IdsPackLevels = exception.IdsPackLevels.OrderBy(x => x.IdsPack.PackIndex).ToList();
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);
}
return requiredLiquids;
}
///
/// Assign the liquid quantities spent by the last job using the job and the handler last status.
///
/// The job.
/// The configuration.
/// The handler.
private List GetJobRunLiquidQuantities(CurrentJobContext context)
{
LogManager.Log($"Calculating job run liquid quantities using '{JobLiquidQuantityCalculationMode}' method...");
var liquidQuantities = new List();
try
{
//if (JobLiquidQuantityCalculationMode == JobLiquidQuantityCalculationMode.MachineStatus)
//{
foreach (var pack in _machineConfiguration.NoneEmptyIdsPacks.ToList())
{
try
{
var packLevelAfter = MachineStatus.IDSPacksLevels.SingleOrDefault(x => x.Index == pack.PackIndex);
var packLevelBefore = context.MachineStatusOnCreation.IDSPacksLevels.SingleOrDefault(x => x.Index == pack.PackIndex);
if (packLevelAfter != null && packLevelBefore != null)
{
if (packLevelAfter.DispenserLevel64 > packLevelBefore.DispenserLevel64)
{
LogManager.Log($"Invalid '{pack.LiquidType.Name}' dispenser level calculated: {packLevelBefore.DispenserLevel64} - {packLevelAfter.DispenserLevel64} = {packLevelBefore.DispenserLevel64 - packLevelAfter.DispenserLevel64}. Ignoring...");
continue;
}
liquidQuantities.Add(new BL.ValueObjects.JobRunLiquidQuantity()
{
LiquidType = pack.LiquidType.Type,
Quantity = packLevelBefore.DispenserLevel64 - packLevelAfter.DispenserLevel64,
});
}
}
catch (Exception ex)
{
LogManager.Log(ex, $"Error calculating liquid quantity for {pack.LiquidType.Name}");
}
}
//}
//else
//{
// liquidQuantities = CreateJobRunLiquidQuantitiesUsingIntegral(job, configuration, processParameters, jobProgress, jobTotalProgress);
//}
LogManager.Log($"Job run liquid quantities calculation completed:\n{liquidQuantities.ToJsonString()}");
}
catch (Exception ex)
{
LogManager.Log(ex, LogCategory.Critical, "Error calculating and saving last job run liquid quantities.");
}
return liquidQuantities;
}
#endregion
#region Public Static Methods
///
/// Creates the job run liquid quantities.
///
/// The job.
/// The configuration.
/// The process parameters.
/// The position.
/// The length.
/// The gradient resolution.
///
public static List CreateJobRunLiquidQuantitiesUsingIntegral(Job job, Configuration configuration, ProcessParametersTable processParameters, double position, double length)
{
var units = Math.Max(job.NumberOfUnits, 1);
var effectiveSegments = new List();
for (int i = 0; i < units; i++)
{
if (i > 0 && job.EnableInterSegment)
{
effectiveSegments.Add(Job.CreateInterSegment(job.InterSegmentLength));
}
foreach (var segment in job.EffectiveSegments)
{
effectiveSegments.Add(segment.Clone(job));
}
}
effectiveSegments.Add(Job.CreateInterSegment(processParameters.DryerBufferLengthMeters));
double total = length;
double position_cm = position * 100d;
double total_length = 0;
Dictionary liquidQuantities = new Dictionary();
foreach (var pack in configuration.NoneEmptyIdsPacks.OrderBy(x => x.PackIndex))
{
liquidQuantities.Add(pack.PackIndex, 0);
}
bool stop_calc = false;
for (int segmentIndex = 0; segmentIndex < effectiveSegments.Count && !stop_calc; segmentIndex++)
{
var segment = effectiveSegments[segmentIndex];
var segment_length_cm = segment.Length * 100d;
List orderedBrushCollection = segment.BrushStops.OrderBy(x => x.OffsetMeters).ToList();
int solid_gradient_oeff = orderedBrushCollection.Count == 1 ? 1 : 2;
double prev_offset_cm = 0;
double delta_brushLenghtToStopPosition = 0d;
double position_interval_centimeters = 0d;//interval for calculation where the stop occurred
for (int brushIndex = 0; brushIndex < orderedBrushCollection.Count && !stop_calc; brushIndex++)
{
var brush = orderedBrushCollection[brushIndex];
double brush_length_centimeters = 0d;
double brush_offset_cm = 0;
if ((brushIndex + 1) < orderedBrushCollection.Count)
{
brush_offset_cm = (brush.OffsetMeters * 100d);
double next_brush_offset_cm = (orderedBrushCollection[brushIndex + 1].OffsetMeters * 100d);
brush_length_centimeters = (next_brush_offset_cm - prev_offset_cm);
double brush_length_centimeters_before_calc = brush_length_centimeters;
if (delta_brushLenghtToStopPosition > 0)//calculate second brush
{
brush_length_centimeters = ((position_interval_centimeters - delta_brushLenghtToStopPosition) * (position_interval_centimeters - delta_brushLenghtToStopPosition)) / position_interval_centimeters;
stop_calc = true;
}
else if (total_length + prev_offset_cm + brush_length_centimeters > position_cm)//calculate first brush
{
position_interval_centimeters = brush_length_centimeters;
delta_brushLenghtToStopPosition = (total_length + prev_offset_cm + brush_length_centimeters) - position_cm;
brush_length_centimeters = brush_length_centimeters - (delta_brushLenghtToStopPosition * delta_brushLenghtToStopPosition / brush_length_centimeters);
}
}
else//last brush or solid brush
{
brush_length_centimeters = (segment_length_cm - prev_offset_cm);
if (delta_brushLenghtToStopPosition > 0)//second brush
{
brush_length_centimeters = ((position_interval_centimeters - delta_brushLenghtToStopPosition) * (position_interval_centimeters - delta_brushLenghtToStopPosition)) / position_interval_centimeters;
stop_calc = true;
}
else if (orderedBrushCollection.Count == 1 && (total_length + segment_length_cm) > position_cm)// solid brush
{
brush_length_centimeters = position_cm - total_length;
stop_calc = true;
}
}
prev_offset_cm = brush_offset_cm;
if (brush.LiquidVolumes != null)
{
foreach (var liquidVolumes in brush.LiquidVolumes)
{
liquidQuantities[liquidVolumes.IdsPack.PackIndex] += liquidVolumes.NanoliterPerCentimeter * (brush_length_centimeters / solid_gradient_oeff);
}
}
}
total_length += segment_length_cm;
}
List quantities = new List();
foreach (var liquidQuantity in liquidQuantities)
{
int index = liquidQuantity.Key;
var idsPack = configuration.NoneEmptyIdsPacks.SingleOrDefault(x => x.PackIndex == index);
if (idsPack != null)
{
quantities.Add(new BL.ValueObjects.JobRunLiquidQuantity()
{
LiquidType = idsPack.LiquidType.Type,
Quantity = (int)liquidQuantities[index],
});
}
}
return quantities;
}
#endregion
#region Public Methods
///
/// 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;
response = await SendRequest(request, new TransportRequestConfig() { ShouldLog = true });
}
catch (Exception 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)
{
_machineConfiguration = configuration;
LogManager.Log("Uploading hardware 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
{
LogManager.Log("Checking if firmware version < 1.6...");
if (DeviceInformation != null && DeviceInformation.Version != null)
{
Version firmwareVersion = Version.Parse(DeviceInformation.Version);
if (firmwareVersion < new Version("1.6.0.0") && firmwareVersion > new Version("1.0.0.0"))
{
LogManager.Log("Firmware version is lower than '1.6'. Skipping hardware configuration upload...");
return new UploadHardwareConfigurationResponse();
}
}
else
{
LogManager.Log("No device information found. Continuing...", LogCategory.Warning);
}
}
catch (Exception ex)
{
LogManager.Log(ex, "Error occurred while checking for firmware version. Continuing...");
}
try
{
LogManager.Log($"Sending '{nameof(UploadHardwareConfigurationRequest)}'...");
LogManager.Log($"{nameof(UploadHardwareConfigurationRequest)} request:\n{request.HardwareConfiguration.ToJsonString()}", LogCategory.Debug);
CurrentHardwareConfiguration = hardwareConfiguration;
response = await SendRequest(request, new TransportRequestConfig() { ShouldLog = false });
}
catch (Exception ex)
{
throw ex;
}
return response;
}
///
/// Starts jogging the specified motor.
///
/// The request.
///
public async Task StartMotorJogging(MotorJoggingRequest request)
{
MotorJoggingResponse response = null;
try
{
response = await SendRequest(request, new TransportRequestConfig() { ShouldLog = true });
}
catch (Exception ex)
{
throw ex;
}
return response;
}
///
/// Stops jogging the specified motor.
///
/// The request.
///
public async Task StopMotorJogging(MotorAbortJoggingRequest request)
{
return await SendRequest(request, new TransportRequestConfig() { ShouldLog = true });
}
///
/// Starts homing the specified motor.
///
/// The request.
///
public IObservable StartMotorHoming(MotorHomingRequest request)
{
return SendContinuousRequest(request, new TransportContinuousRequestConfig() { ContinuousTimeout = ContinuousRequestTimeout, ShouldLog = true }).Select(x => x.Message);
}
///
/// Stops homing the specified motor.
///
/// The request.
///
public async Task StopMotorHoming(MotorAbortHomingRequest request)
{
return await SendRequest(request, new TransportRequestConfig() { ShouldLog = true });
}
///
/// Starts jogging the specified dispenser.
///
/// The request.
///
public async Task StartDispenserJogging(DispenserJoggingRequest request)
{
return await SendRequest(request, new TransportRequestConfig() { ShouldLog = true });
}
///
/// Stops jogging the specified dispenser.
///
/// The request.
///
public async Task StopDispenserJogging(DispenserAbortJoggingRequest request)
{
return await SendRequest(request, new TransportRequestConfig() { ShouldLog = true });
}
///
/// Starts homing the specified dispenser.
///
/// The request.
///
public IObservable StartDispenserHoming(DispenserHomingRequest request)
{
return SendContinuousRequest(request, new TransportContinuousRequestConfig() { ContinuousTimeout = ContinuousRequestTimeout, ShouldLog = true }).Select(x => x.Message);
}
///
/// Stops homing the specified dispenser.
///
/// The request.
///
public async Task StopDispenserHoming(DispenserAbortHomingRequest request)
{
return await SendRequest(request, new TransportRequestConfig() { ShouldLog = true });
}
///
/// Turn on/off the specified digital output pin.
///
/// The request.
///
public async Task SetDigitalOut(SetDigitalOutRequest request)
{
return await SendRequest(request, new TransportRequestConfig() { ShouldLog = true });
}
///
/// Starts jogging the thread motion system.
///
/// The request.
///
public async Task StartThreadJogging(ThreadJoggingRequest request)
{
return await SendRequest(request, new TransportRequestConfig() { ShouldLog = true });
}
///
/// Stops jogging the thread motion system.
///
/// The request.
///
public async Task StopThreadJogging(ThreadAbortJoggingRequest request)
{
return await SendRequest(request, new TransportRequestConfig() { ShouldLog = true });
}
///
/// Sets the specified component value.
///
/// The request.
///
public async Task SetComponentValue(SetComponentValueRequest request)
{
return await SendRequest(request, new TransportRequestConfig() { ShouldLog = true });
}
///
/// 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
{
response = await SendRequest(request, new TransportRequestConfig() { ShouldLog = true });
}
catch (Exception 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
{
response = await SendRequest(request, new TransportRequestConfig() { ShouldLog = true });
}
catch (Exception 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
{
response = await SendRequest(request, new TransportRequestConfig() { ShouldLog = true });
}
catch (Exception 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 };
return await SendRequest(request, new TransportRequestConfig() { ShouldLog = true });
}
///
/// Resets the embedded device.
///
///
public async Task Reset()
{
StubFpgaWriteRegResponse response = null;
StubFpgaWriteRegRequest request = null;
try
{
request = new StubFpgaWriteRegRequest()
{
Address = 0x60000800 | 0x3D0,
Value = 0x0
};
response = await SendRequest(request, new TransportRequestConfig() { ShouldLog = true });
}
catch (Exception ex)
{
throw ex;
}
Thread.Sleep(1000);
try
{
request = new StubFpgaWriteRegRequest()
{
Address = 0x60000800 | 0x3D0,
Value = 0x1
};
response = await SendRequest(request, new TransportRequestConfig() { ShouldLog = true });
}
catch (Exception ex)
{
throw ex;
}
return response;
}
///
/// Directs the embedded device to switch to stand-by mode.
///
///
public async Task StandBy()
{
LogManager.Log("Switching to stand-by mode...");
await SendRequest(new StandByRequest(), new TransportRequestConfig() { ShouldLog = true });
}
///
/// 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).
/// Specify whether the connected machine is emulated and to skip the actual DFU interface.
///
///
public virtual async Task UpgradeFirmware(Stream tfpStream, bool isEmulated = false)
{
bool cancel = false;
ZipFile zip = null;
Action abortAction = null;
var upgradeHandler = new FirmwareUpgradeHandler(() =>
{
cancel = true;
abortAction?.Invoke();
});
try
{
LogManager.Log("Starting firmware upgrade...");
LogManager.Log($"Firmware upgrade flags: {String.Join(", ", FirmwareUpgradeMode.GetFlags().Select(x => x.ToString()))}");
if (!CanPrint)
{
throw LogManager.Log(new InvalidOperationException($"Could not perform firmware upgrade while operator status is '{Status}'."));
}
LogManager.Log("Extracting tfp package...");
var package_info = await GetFirmwarePackageInfo(tfpStream);
tfpStream.Position = 0;
LogManager.Log("Validating TFP package...");
package_info.Validate();
LogManager.Log("Reading zip stream...");
zip = ZipFile.Read(tfpStream);
double packageUploadTotal = 0;
try
{
packageUploadTotal = zip.Entries.Sum(x => x.UncompressedSize);
}
catch (Exception ex)
{
LogManager.Log(ex, "Error calculating total package upload bytes.");
}
LogManager.Log("Creating storage manager...");
var storage = CreateStorageManager();
LogManager.Log("Getting storage drive information...");
var drive = await storage.GetStorageDrive();
LogManager.Log($"Storage drive info:\n{drive.ToJsonString()}");
LogManager.Log("Getting root folder...");
var root = await storage.GetRootFolder();
LogManager.Log($"Root folder: '{root.Path}'");
var existing_folder = root.Items.SingleOrDefault(x => x.Name == FIRMWARE_UPGRADE_FOLDER_NAME);
if (existing_folder != null)
{
LogManager.Log("Root folder exists. Deleting...");
await storage.DeleteItem(existing_folder);
}
String package_folder = Path.Combine(drive.Root, FIRMWARE_UPGRADE_FOLDER_NAME);
LogManager.Log($"Creating new folder: '{package_folder}'.");
await storage.CreateFolder(package_folder);
List handlers = new List();
List entries = zip.Entries.ToList();
List streams = new List();
LogManager.Log("Disabling keep alive...");
var keepAlive = UseKeepAlive;
UseKeepAlive = false;
Action upgradeDFU = null;
Action uploadNext = null;
Action validate = null;
Action activate = null;
Action postActivation = null;
Action upgradeEureka = null;
UpdateStatus(MachineStatuses.Upgrading);
abortAction = new Action(() =>
{
UpdateStatus(MachineStatuses.ReadyToDye);
});
upgradeDFU = new Action(() =>
{
try
{
if (FirmwareUpgradeMode.HasFlag(FirmwareUpgradeModes.DFU))
{
if (package_info.ContainsMcu())
{
LogManager.Log("DFU enabled. Starting upgrade via DFU...");
LogManager.Log("Extracting MCU file...");
ZipEntry mcuEntry = null;
try
{
mcuEntry = entries.Single(x => x.FileName == package_info.FileDescriptors.Single(y => y.Destination == VersionFileDestination.Mcu).FileName);
entries.Remove(mcuEntry);
}
catch (Exception ex)
{
LogManager.Log(ex, "Error extracting MCU file from package.");
upgradeHandler.RaiseFailed(new IOException("Error retrieving MCU file from package.", ex));
return;
}
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.RaiseProgress(FirmwareUpgradeStatus.Upgrading, e.State.ToDescription(), false, e.Total, e.Progress);
};
LogManager.Log("Disconnecting adapter...");
Adapter.Disconnect().Wait();
ResetEvents();
ResetInkFllingStatus();
try
{
if (!isEmulated)
{
LogManager.Log("Upgrading...");
upgradeManager.PerformUpgrade(data).Wait();
}
else
{
LogManager.Log("Upgrading (emulated)...");
Thread.Sleep(3000);
}
}
catch (Exception ex)
{
LogManager.Log("Firmware upgrade failed while doing DFU upload. We need to destroy the whole transport layer.", LogCategory.Error);
UpdateStatus(MachineStatuses.Disconnected);
upgradeHandler.RaiseFailed(ex);
OnFailed(ex);
return;
}
LogManager.Log("Waiting for the device...");
upgradeHandler.RaiseProgress(FirmwareUpgradeStatus.Upgrading, "Waiting for the device...");
Thread.Sleep(5000);
LogManager.Log("Reconnecting adapter...");
upgradeHandler.RaiseProgress(FirmwareUpgradeStatus.Upgrading, "Connecting...");
Adapter.Connect().Wait();
Connect().Wait();
LogManager.Log("Connected...");
upgradeHandler.RaiseProgress(FirmwareUpgradeStatus.Upgrading, "Connected.");
Thread.Sleep(2000);
LogManager.Log("Waiting...");
upgradeHandler.RaiseProgress(FirmwareUpgradeStatus.Upgrading, "Waiting...");
Thread.Sleep(2000);
UpdateStatus(MachineStatuses.Upgrading);
}
else
{
LogManager.Log("DFU is enabled but no MCU file was found on the package. Skipping...");
}
}
//Upload tfp package only if specified in flag && package info contains more files other than the mcu bin file.
if (FirmwareUpgradeMode.HasFlag(FirmwareUpgradeModes.TFP_PACKAGE))
{
if (package_info.ContainsNoneMcu())
{
LogManager.Log("TFP package is enabled. Starting upload...");
uploadNext();
}
else
{
LogManager.Log("TFP package is enabled but no other files other than the MCU file were found on the package. Skipping...");
postActivation();
}
}
else
{
postActivation();
}
}
catch (Exception ex)
{
UpdateStatus(MachineStatuses.ReadyToDye);
upgradeHandler.RaiseFailed(ex);
return;
}
});
upgradeEureka = new Action(() =>
{
try
{
if (FirmwareUpgradeMode.HasFlag(FirmwareUpgradeModes.DFU))
{
if (package_info.ContainsMcu())
{
LogManager.Log("Firmware upgrade enabled. Starting upgrade via file system...");
var upgradeDrive = DriveInfo.GetDrives().FirstOrDefault(x => EUREKA_FIRMWARE_UPGRADE_DRIVE_LABELS.Contains(x.VolumeLabel));
if (upgradeDrive == null)
{
throw LogManager.Log(new IOException($"Could not locate any of the firmware upgrade volume labels ({EUREKA_FIRMWARE_UPGRADE_DRIVE_LABELS.Join(", ")})"));
}
String upgradeFolder = upgradeDrive.RootDirectory.FullName;
LogManager.Log("Extracting MCU file...");
ZipEntry mcuEntry = null;
try
{
mcuEntry = entries.Single(x => x.FileName == package_info.FileDescriptors.Single(y => y.Destination == VersionFileDestination.Mcu).FileName);
entries.Remove(mcuEntry);
}
catch (Exception ex)
{
LogManager.Log(ex, "Error extracting MCU file from package.");
upgradeHandler.RaiseFailed(new IOException("Error retrieving MCU file from package.", ex));
return;
}
LogManager.Log("Disconnecting adapter...");
Adapter.Disconnect().Wait();
ResetEvents();
ResetInkFllingStatus();
try
{
if (!isEmulated)
{
LogManager.Log("Upgrading...");
mcuEntry.Extract(upgradeFolder, ExtractExistingFileAction.OverwriteSilently);
}
else
{
LogManager.Log("Upgrading (emulated)...");
Thread.Sleep(3000);
}
}
catch (Exception ex)
{
LogManager.Log(ex, "Firmware upgrade failed while extracting the MCU file to the upgrade location.");
UpdateStatus(MachineStatuses.Disconnected);
upgradeHandler.RaiseFailed(ex);
OnFailed(ex);
return;
}
LogManager.Log("Waiting for the device...");
upgradeHandler.RaiseProgress(FirmwareUpgradeStatus.Upgrading, "Waiting for the device...");
Thread.Sleep(5000);
LogManager.Log("Reconnecting adapter...");
upgradeHandler.RaiseProgress(FirmwareUpgradeStatus.Upgrading, "Connecting...");
Adapter.Connect().Wait();
Connect().Wait();
LogManager.Log("Connected...");
upgradeHandler.RaiseProgress(FirmwareUpgradeStatus.Upgrading, "Connected.");
Thread.Sleep(2000);
LogManager.Log("Waiting...");
upgradeHandler.RaiseProgress(FirmwareUpgradeStatus.Upgrading, "Waiting...");
Thread.Sleep(2000);
UpdateStatus(MachineStatuses.Upgrading);
}
else
{
LogManager.Log("DFU is enabled but no MCU file was found on the package. Skipping...");
}
}
//Upload tfp package only if specified in flag && package info contains more files other than the mcu bin file.
if (FirmwareUpgradeMode.HasFlag(FirmwareUpgradeModes.TFP_PACKAGE))
{
if (package_info.ContainsNoneMcu())
{
LogManager.Log("TFP package is enabled. Starting upload...");
uploadNext();
}
else
{
LogManager.Log("TFP package is enabled but no other files other than the MCU file were found on the package. Skipping...");
postActivation();
}
}
else
{
postActivation();
}
}
catch (Exception ex)
{
UpdateStatus(MachineStatuses.ReadyToDye);
upgradeHandler.RaiseFailed(ex);
return;
}
});
uploadNext = new Action(() =>
{
if (entries.Count > 0)
{
try
{
var entry = entries.First();
entries.Remove(entry);
LogManager.Log($"Uploading file '{entry.FileName}'...");
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(FirmwareUpgradeStatus.Uploading, $"Uploading '{entry.FileName}'...", false, packageUploadTotal, upgradeHandler.Current + e.Delta);
};
}
catch (Exception ex)
{
abortAction();
upgradeHandler.RaiseFailed(ex);
}
}
else
{
validate();
}
});
validate = new Action(() =>
{
try
{
LogManager.Log("Validating version...");
streams.ForEach(x => x.Dispose());
upgradeHandler.RaiseProgress(FirmwareUpgradeStatus.Validating, "Validating version...");
var validateRequest = new ValidateVersionRequest();
validateRequest.Path = package_folder;
var validateResponse = SendRequest(validateRequest, new TransportRequestConfig() { Timeout = TimeSpan.FromSeconds(10), ShouldLog = true }).Result;
activate();
}
catch (Exception ex)
{
upgradeHandler.RaiseFailed(ex);
}
});
activate = new Action(() =>
{
try
{
TaskCompletionSource