using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using LaaPackages.Features.Cordonel.Models;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisPwd;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisStatus;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
using Xylem.Common.Logic.ProductionOrderCore;
using Xylem.Common.Production.ProductionUiCordonel.Properties;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.Enum;
using Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses.EventArgs;
using Xylem.Common.Production.ProductionUiCordonel.UserControls;
using Xylem.Common.Utils.ProcessExec;
using Xylem.Common.Utils.ProcessExec.EventArguments;
namespace Xylem.Common.Production.ProductionUiCordonel.ProductionProcesses
{
///
/// The base production process defines the tools needed to initiate, start and finalize a process. It holds the
/// statistic counters.
///
public abstract class BaseProductionProcess : IProductionProcess
{
#region ------------------------------------------ Variables --------------------------------------------------
// remind manually changed culture setting
private static readonly CultureInfo _cultureInfo = CultureInfo.CurrentUICulture;
// user defined control for main screen
protected UserControl UserControl
{
get; set;
}
///
/// Avoid state change after successfully completion of production process. This will be used if multiple
/// processes are executed and the state has been changed manually.
///
public static readonly ProcessState? DoNotChangeStateAfterCompletion = null;
///
/// Number of processing steps for this actual process progress
///
private Int32 _maxActualProcessSteps;
private Int32 MaxActualProcessProgress
{
get => _maxActualProcessSteps;
set
{
_maxActualProcessSteps = value;
OnProcessProgressChanged?.Invoke(this, new ProcessProgressArgs(_maxActualProcessSteps,
ProcessProgressArgs.ProcessProgressType.MaxActualProcessSteps));
}
}
///
/// Progress of actual process
///
private Int32 _actualProcessProgress;
protected Int32 ActualProcessProgress
{
get => _actualProcessProgress;
set
{
_actualProcessProgress = value;
if (_actualProcessProgress > _maxActualProcessSteps)
_actualProcessProgress = _maxActualProcessSteps;
OnProcessProgressChanged?.Invoke(this, new ProcessProgressArgs(_actualProcessProgress,
ProcessProgressArgs.ProcessProgressType.ActualProcessSteps));
}
}
///
public SingleProcessState SingleProcessState
{
get; private set;
}
///
/// The forced exit state will be used by e.g. the error process to keep the standard handling
/// intact but switch to a dynamic (e.g. based on a dialog input) exit which is not predictable
/// by the standard state machine implementation.
///
protected ProcessState? ForcedSuccessExitState
{
set; get;
}
///
public String ProcessName
{
get;
}
///
public Version Version => _version;
///
public GenesisStatus ProductionStatus
{
set; protected get;
}
///
public CordonelRequirements ProductionRequirements
{
set; protected get;
}
///
public EOLProgressModel EolProgress
{
set; protected get;
}
///
public IGenesisMeter Meter
{
get; set;
}
///
public RegisterRestorer RegisterRestorer
{
protected get; set;
}
///
public List RadioEncryptionKey
{
protected get; set;
}
///
public MeterPwdDb PwdContainer
{
set; protected get;
}
///
/// Is for processes which do not want to log the execution or state change like ERROR process,
///
protected Boolean IsSilentProcess
{
set; get;
}
// assembly version information
private static readonly Version _version = Assembly.GetExecutingAssembly().GetName().Version;
///
public CancellationToken CancellationToken { set; get; }
///
public event EventHandler OnProcessStateChanged;
///
public event EventHandler OnForceStateMachineStateChange;
///
public event EventHandler OnProcessLogRequest;
///
public event EventHandler OnProcessProgressChanged;
///
public event EventHandler OnGuiMessageDispatcher;
#endregion --------------------------------------- Variables --------------------------------------------------
#region ------------------------------------------ Init -------------------------------------------------------
///
/// Ctor:
/// - Init.
///
/// name for process to display a message
///
/// - Initial
///
protected BaseProductionProcess(String name)
{
ProcessName = name;
SingleProcessState = SingleProcessState.Waiting;
}
#endregion --------------------------------------- Init -------------------------------------------------------
#region ------------------------------------------ Tools ------------------------------------------------------
///
/// Set overall process to 100 %
///
protected void SetProcessProgressTo100Percent()
{
OnProcessProgressChanged?.Invoke(this, new ProcessProgressArgs(Int32.MaxValue,
ProcessProgressArgs.ProcessProgressType.OverallProcessesSteps));
}
///
public UserControl GetUserControl()
{
// Invoker needed for UserControls
Application.Current.Dispatcher.Invoke(InitUserControl);
// Null as return is going to be used to remove the userControl from main screen
return UserControl;
}
///
/// Common routine for single register write, log of result and increase process counter.
///
///
/// true if successfully
///
/// - Initial
///
///
/// - Inverse raw register bytes before conversion to text using the
/// .
///
protected Boolean WriteRegisterLogAndProcessCtr(ProgrammingParameters proPar)
{
var retVal = false;
var msg = "?";
try
{
// write without check
retVal = Meter.WriteRegister(proPar.RegisterName, proPar.RegisterValue);
// convert to readable value
var registerDefinitions = Meter.GetConfigRegistersDefinitions();
var regDef = registerDefinitions.GetRegisterDefinitionByName(proPar.RegisterName);
msg = RegisterConverter.GetRegisterContentText(regDef, proPar.RegisterValue);
}
catch (Exception ex)
{
Meter.WriteLog(ex.Message);
}
finally
{
if (retVal)
{
SuccessMsgDispatcher($"{Resources.StrSuccessMsgWrite}: {msg}");
}
else
{
ErrorMsgDispatcher($"{Resources.StrErrorMsgWrite}: {msg}");
}
}
ActualProcessProgress++;
return retVal;
}
///
/// Action for cancellation token to abort process
///
///
/// - Initial
///
protected StatusReturn CancellationProcedure()
{
AbortProcess();
return StatusReturn.Failed;
}
///
/// Set new single process state and fire event from base class:
/// - First set new SingleProcessState,
/// - Last fire the SingleProcessState.
///
/// ATTENTION: Made it private to avoid INTERRUPTION of state machine!
/// This is caused by the eventual removal of the process events using the OnSingleProcessStateChanged.
///
///
///
///
///
/// - Initial
///
///
/// - Set single process state for calling production process,
/// - fire single process state event.
///
///
/// - PreExecuteProcess.
///
private void SetAndSignalProcessState(Object sender, SingleProcessState state)
{
SingleProcessState = state;
OnProcessStateChanged?.Invoke(sender, new SingleProcessStateArgs(state));
}
///
/// Fire event for GUI messages
///
///
protected void DispatchGuiMessage(GuiMessageArgs args)
{
OnGuiMessageDispatcher?.Invoke(this, args);
}
///
public void StartProcess(ProcessState? successExitState, ProcessState errorExitState = ProcessState.Error,
ProcessState breakExitState = ProcessState.AbortTest)
{
if (Meter == null)
{
// First signal to change the state machine because the single process state change uninstalls
// all events from process
OnForceStateMachineStateChange?.Invoke(this, new ProcessStateArgs(ProcessState.Error));
// Set process state for this process and signal it to final controller
SetAndSignalProcessState(this, SingleProcessState.Error);
}
var userControl = (IUserControl)UserControl;
userControl?.Clear();
var exitProcessStateObjects = new Object[3];
exitProcessStateObjects[0] = successExitState;
exitProcessStateObjects[1] = errorExitState;
exitProcessStateObjects[2] = breakExitState;
var processExec = new ProcessExec();
processExec.NewProcess(InitProcess, PreExecuteProcess, ExecuteProcess, FinalizeProcess,
_cultureInfo, exitProcessStateObjects, CancellationToken);
}
///
public virtual void InitProcess()
{
// Set process state for this process and signal it to final controller
SetAndSignalProcessState(this, SingleProcessState.Waiting);
}
///
public virtual void IdleProcess()
{
// set process state for this process and signal it to final controller
SetAndSignalProcessState(this, SingleProcessState.Idle);
}
///
public virtual StatusReturn PreExecuteProcess()
{
return StatusReturn.Okay;
}
///
public virtual void AbortProcess()
{
// set process state for this process and signal it to final controller
SetAndSignalProcessState(this, SingleProcessState.Abort);
}
///
public virtual void RetryProcess()
{
// set process state for this process and signal it to final controller
SetAndSignalProcessState(this, SingleProcessState.Retry);
}
///
public abstract StatusReturn ExecuteProcess();
///
public virtual void FinalizeProcess(StatusReturn success, IReadOnlyList