Safe wrappers in ProcessData, clean-up.
This commit is contained in:
parent
8d640bb52a
commit
2c91ded0db
@ -1,857 +0,0 @@
|
||||
///
|
||||
/// Copyright (c) 2013-2021 Sensus Slovensko a.s.
|
||||
///
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
using log4net;
|
||||
using NHibernate;
|
||||
using Common;
|
||||
using Config.Entities;
|
||||
using TBF.BenchControl.Generic;
|
||||
using TBF.BenchControl.GenericDevices;
|
||||
using TBF.BenchControl.Sequences;
|
||||
using Dirichlet.Numerics;
|
||||
using TBF.Resources;
|
||||
|
||||
namespace TBF.BenchControl
|
||||
{
|
||||
public enum MachineState
|
||||
{
|
||||
Undefined = 0,
|
||||
Disabled,
|
||||
StartingUp, /// Start-up: Initialize() of all devices + start of the worker thread
|
||||
FailedToStart, /// Most likely Initialize() of any device thrown an exception
|
||||
Running, /// RunDeviceBefore() and RunDeviceAfter() of all devices, Start()/Run()/Stop() of operations
|
||||
ShuttingDown, /// Shutdown: StopDevice() and StopDevice2() of all devices
|
||||
Off, /// Off after the shutdown
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This class controls real test bench behavior.
|
||||
/// It is based on a state machine
|
||||
/// </summary>
|
||||
public static class StateMachine
|
||||
{
|
||||
private static readonly ILog log = LogManager.GetLogger(typeof(StateMachine));
|
||||
private static readonly ILog wlog = LogManager.GetLogger(typeof(StateMachine));
|
||||
|
||||
/// Private devices and components
|
||||
static IList<IComponent> components; /// list of all components
|
||||
static IList<IDevice> devices; /// list of devices
|
||||
///
|
||||
/// Bench paths
|
||||
static IList<Config.Entities.FeedingPath> feedingPaths;
|
||||
static IList<Config.Entities.BenchPath> benchPaths;
|
||||
static IList<Config.Entities.OutputPath> outputPaths;
|
||||
static IList<Config.Entities.MetersPath> metersPaths;
|
||||
static IList<Config.Entities.HeatMetersPath> heatMetersPaths;
|
||||
public static IList<Config.Entities.TransitionSequence> TransitionSequences;
|
||||
public static IList<Config.Entities.TransitionStep> TransitionSteps;
|
||||
|
||||
/// Public components
|
||||
public static Elde.ControlBoardDev ControlBoard;
|
||||
public static GenericDevices.IAmbient Ambient;
|
||||
public static IOperation BenchWaitingOp;
|
||||
public static IOperation BenchErrorOp;
|
||||
public static GenericDevices.IScaleOrTank Tank1;
|
||||
public static GenericDevices.IScaleOrTank Tank2;
|
||||
public static GenericDevices.IScaleOrTank Tank3;
|
||||
public static GenericDevices.IValve DrainValve1;
|
||||
public static GenericDevices.IValve DrainValve2;
|
||||
public static GenericDevices.IValve DrainValve3;
|
||||
public static IList<IValve> MasterValves; /// list of directly controlled valves
|
||||
public static IList<IValve> CoupledValves; /// list of coupled valves
|
||||
public static IList<Elde.ValveEx.Valve> ExtendedValves; /// list of extended valves
|
||||
|
||||
public static IList<string> LoopStartNames;
|
||||
public static IList<string> LoopEndNames;
|
||||
|
||||
/// Time and synchronization
|
||||
#if TURA_IPERL || TURA_IPERL_NEW
|
||||
public const int Period = 2; /// State machine period in sec.
|
||||
#else
|
||||
public const int Period = 1; /// State machine period in sec.
|
||||
#endif
|
||||
|
||||
static DateTime startDateTime; /// DateTime of time instance when the state machine worker thread starts
|
||||
static int currentTimeSec; /// Time from the start of the state machine in seconds
|
||||
|
||||
public static MachineState MachineState; /// see enum MachineState for explanation
|
||||
|
||||
/// Worker thread and database session
|
||||
static Thread workerThread;
|
||||
|
||||
static IList<State> states; /// list of states
|
||||
|
||||
public static DateTime CycleStartTimeStamp;
|
||||
|
||||
public static IList<IValve> DefaultValvesOpen
|
||||
{
|
||||
get
|
||||
{
|
||||
return GenericDevices.ValveBase.Merge(
|
||||
Utils.ValvesOpen((feedingPaths != null && feedingPaths.Count > 0) ? feedingPaths[0] : null),
|
||||
Utils.ValvesOpen((benchPaths != null && benchPaths.Count > 0) ? benchPaths[0] : null),
|
||||
Utils.ValvesOpen((outputPaths != null && outputPaths.Count > 0) ? outputPaths[0] : null)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static IList<IValve> DefaultValvesClose
|
||||
{
|
||||
get
|
||||
{
|
||||
return GenericDevices.ValveBase.Merge(
|
||||
Utils.ValvesClose((feedingPaths != null && feedingPaths.Count > 0) ? feedingPaths[0] : null),
|
||||
Utils.ValvesClose((benchPaths != null && benchPaths.Count > 0) ? benchPaths[0] : null),
|
||||
Utils.ValvesClose((outputPaths != null && outputPaths.Count > 0) ? outputPaths[0] : null)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loaded by LoadProcedure() or IOperation LoadProcedureOp(...)
|
||||
/// </summary>
|
||||
public static Procedure Procedure; /// Procedure
|
||||
public static TestInstance[] TestInstances; /// Tests
|
||||
public static bool IsRemoteProcedure;
|
||||
|
||||
|
||||
/// Hardware devices connected to the PC controlling the bench.
|
||||
public static IList<IComponent> Components { get { return components; } }
|
||||
public static IList<IDevice> Devices { get { return devices; } }
|
||||
|
||||
/// <summary>
|
||||
/// DateTime of time instance when the state machine worker thread starts
|
||||
/// </summary>
|
||||
public static DateTime StartDateTime { get { return startDateTime; } }
|
||||
|
||||
/// <summary>
|
||||
/// Current state name
|
||||
/// </summary>
|
||||
public static int Time
|
||||
{
|
||||
get { return currentTimeSec; }
|
||||
set { currentTimeSec = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
static StateMachine()
|
||||
{
|
||||
devices = new List<IDevice>();
|
||||
states = new List<State>();
|
||||
currentTimeSec = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a device to the state machine.
|
||||
/// </summary>
|
||||
/// <param name="obj">Device</param>
|
||||
public static void AddDevice(IDevice device)
|
||||
{
|
||||
if (device != null && !devices.Contains(device)) devices.Add(device);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a state to the state machine.
|
||||
/// In this way a sequence can be created programtically.
|
||||
/// </summary>
|
||||
/// <param name="obj">State</param>
|
||||
public static void AddState(State state)
|
||||
{
|
||||
if (state != null && !states.Contains(state)) states.Add(state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the state from the state machine.
|
||||
/// </summary>
|
||||
/// <param name="obj">State</param>
|
||||
public static void RemoveState(State state)
|
||||
{
|
||||
if (state != null && state != State.CurrentState && states.Contains(state)) states.Remove(state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a state from a label
|
||||
/// </summary>
|
||||
/// <param name="label"></param>
|
||||
/// <returns>The matching state or null</returns>
|
||||
static State GetStateFromLabel(string label)
|
||||
{
|
||||
if (label == null) return null;
|
||||
|
||||
foreach (State state in states)
|
||||
{
|
||||
if (state.Label != null && state.Label.Equals(label)) return state;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start the state machine in the state 'label' in a desired mode of operation.
|
||||
/// This method is called in the UI thread and creates a new state machine thread.
|
||||
/// This method call should be embedded in: try { StateMachine.Start(...); } catch { }
|
||||
/// to handle configuration problems. Calls CreateDevices(mode) and CreateStates().
|
||||
/// </summary>
|
||||
/// <param name="mode">Mode of operation</param>
|
||||
/// <param name="benchData">A copy of bench data used by the state machine</param>
|
||||
/// <param name="label">Identifies the initial state</param>
|
||||
#if DN100
|
||||
public static void InitializeBoardEtc(ControlCom2VB.ControlCom2panel ctrlBrdComponent)
|
||||
#elif FUZHOU_150 || MUNICH
|
||||
public static void InitializeBoardEtc(ControlComponent3Munich.UserControl1 ctrlBrdComponent)
|
||||
#elif FUZHOU_300
|
||||
public static void InitializeBoardEtc(ControlComponent3F300.UserControl1 ctrlBrdComponent)
|
||||
#elif BERLIN || GELSENWASSER || GENESIS || IZRAEL_200 || PETERSBURG_200 || PUCHONG_200 || SLM_150
|
||||
public static void InitializeBoardEtc(ControlComponent_Izrael2014.UserControl1 ctrlBrdComponent)
|
||||
#else /// all newer benches
|
||||
public static void InitializeBoardEtc(ControlComponent_Torino2015.UserControl1 ctrlBrdComponent)
|
||||
#endif
|
||||
{
|
||||
/// Load the list of components (entities) from the database.
|
||||
/// Then create the components (derived from IComponent).
|
||||
components = BenchControl.TbfComponents.LoadComponentsFromDB(TBF.DB.CreateSession(Common.DBKind.Config));
|
||||
MasterValves = GenericDevices.ValveBase.MasterValves(components);
|
||||
CoupledValves = GenericDevices.ValveBase.CoupledValves(components);
|
||||
ExtendedValves = GenericDevices.ValveBase.ExtendedValves(components);
|
||||
|
||||
/// Find all balances (to initialize tank capacities in the control board)
|
||||
/// Find the control board
|
||||
IList<IScaleOrTank> tanks = new List<IScaleOrTank>();
|
||||
ProcessData.IperlHeads = new List<TestMethods.iPerlCommunication.iPerlHead.IperlHead>();
|
||||
SequenceBase.FlowMeters = new List<IFlowMeter>();
|
||||
SequenceBase.RegulValves = new List<IRegValve>();
|
||||
SequenceBase.FeedingRegulValves = new List<IRegValve>();
|
||||
SequenceBase.OutputRegulValves = new List<IRegValve>();
|
||||
SequenceBase.PumpsWithFM = new List<IPumpFM>();
|
||||
SequenceBase.WaterMeters = new List<IWaterMeter>();
|
||||
SequenceBase.Cameras = new List<ICamera>();
|
||||
LoopStartNames = new List<string>();
|
||||
LoopEndNames = new List<string>();
|
||||
UInt128 valvesToInvert = 0;
|
||||
foreach (var cmpnt in components)
|
||||
{
|
||||
if (cmpnt is Elde.ControlBoardDev) ControlBoard = cmpnt as Elde.ControlBoardDev;
|
||||
if (cmpnt is IBenchInfo) ProcessData.BenchInfo = cmpnt as IBenchInfo;
|
||||
if (cmpnt is IErrorFlags) ProcessData.ErrorFlagsComp = cmpnt as IErrorFlags;
|
||||
if (cmpnt is IStatisticsMonitoring) ProcessData.StatisticsMonitoringComp = cmpnt as IStatisticsMonitoring;
|
||||
|
||||
if ((cmpnt is Output.DB.SensusOracle.Database) && (ProcessData.OracleDB == null))
|
||||
{
|
||||
/// The 1st Oracle database component found --> store the reference
|
||||
ProcessData.OracleDB = cmpnt as Output.DB.SensusOracle.Database;
|
||||
}
|
||||
if ((cmpnt is Output.DB.ProductionTracing.Tracing) && (ProcessData.TracingDB == null))
|
||||
{
|
||||
/// The 1st production tracing database component found --> store the reference
|
||||
ProcessData.TracingDB = cmpnt as Output.DB.ProductionTracing.Tracing;
|
||||
}
|
||||
|
||||
if (cmpnt is TestMethods.iPerlCommunication.iPerlHead.IperlHead)
|
||||
{
|
||||
ProcessData.IperlHeads.Add(cmpnt as TestMethods.iPerlCommunication.iPerlHead.IperlHead);
|
||||
}
|
||||
if (cmpnt is IFlowMeter) SequenceBase.FlowMeters.Add(cmpnt as IFlowMeter);
|
||||
if ((cmpnt is IRegValve) && !(cmpnt is BenchControl.Elde.RegulValveTandem.RegulValveTandem))
|
||||
{
|
||||
IRegValve rv = cmpnt as IRegValve;
|
||||
SequenceBase.RegulValves.Add(rv);
|
||||
if (rv.Category == ValveCategory.Feeding) SequenceBase.FeedingRegulValves.Add(rv);
|
||||
if (rv.Category == ValveCategory.Output) SequenceBase.OutputRegulValves.Add(rv);
|
||||
}
|
||||
if (cmpnt is IPumpFM && !(cmpnt is Elde.PumpTandem.Pump))
|
||||
{
|
||||
SequenceBase.PumpsWithFM.Add(cmpnt as IPumpFM);
|
||||
}
|
||||
if (cmpnt is IWaterMeter) SequenceBase.WaterMeters.Add(cmpnt as IWaterMeter);
|
||||
if (cmpnt is ICamera) SequenceBase.Cameras.Add(cmpnt as ICamera);
|
||||
if (cmpnt is GenericDevices.IAmbient) Ambient = cmpnt as GenericDevices.IAmbient;
|
||||
|
||||
if (cmpnt is GenericDevices.IParallelOutput && cmpnt.Name.ToLower().Contains("tower"))
|
||||
{
|
||||
BenchWaitingOp = (cmpnt as GenericDevices.IParallelOutput).ShowBenchWaitingOp();
|
||||
BenchErrorOp = (cmpnt as GenericDevices.IParallelOutput).ShowBenchErrorOp();
|
||||
}
|
||||
|
||||
if (cmpnt is ITestMethod && cmpnt.ClassName == "TestMethods.OuterLoop.Start") LoopStartNames.Add(cmpnt.Name);
|
||||
if (cmpnt is ITestMethod && cmpnt.ClassName == "TestMethods.OuterLoop.End") LoopEndNames.Add(cmpnt.Name);
|
||||
|
||||
if (cmpnt is IScaleOrTank)
|
||||
{
|
||||
IScaleOrTank tank = cmpnt as IScaleOrTank;
|
||||
tanks.Add(tank);
|
||||
if (Tank1 == null) Tank1 = tank;
|
||||
else if (Tank2 == null) Tank2 = tank;
|
||||
else if (Tank3 == null) Tank3 = tank;
|
||||
}
|
||||
|
||||
Elde.Valve.Valve eldeValve = (cmpnt as Elde.Valve.Valve);
|
||||
if ((eldeValve != null) && eldeValve.Inverted) valvesToInvert |= eldeValve.Mask;
|
||||
}
|
||||
|
||||
/// Pre-initialize the control board (= buffer the arguments ctrlBrdComponent, tankCapacities)
|
||||
if (ControlBoard != null)
|
||||
{
|
||||
/// Create an array with tank capacities from 'tanks' list
|
||||
double[] tankCapacities = new double[tanks.Count];
|
||||
for (int i = 0; i < tankCapacities.Length; i++) tankCapacities[i] = tanks[i].Capacity;
|
||||
|
||||
ControlBoard.InitializeEldeComponent(ctrlBrdComponent, valvesToInvert, tankCapacities);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Control board component is missing");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static string CurrentlyInitializedComponentName;
|
||||
///
|
||||
public static string InitializeComponents()
|
||||
{
|
||||
CurrentlyInitializedComponentName = "-";
|
||||
|
||||
bool anyComponentIsInSimulMode = false;
|
||||
StringBuilder inSimulMode = new StringBuilder();
|
||||
|
||||
/// Add all devices to the state machine and initialize them
|
||||
foreach (var cmpnt in components)
|
||||
{
|
||||
if (cmpnt.DebugLevel == DebugMode.Simulate)
|
||||
{
|
||||
inSimulMode.AppendFormat("{0}{1}", anyComponentIsInSimulMode ? ", " : "", cmpnt.Name);
|
||||
anyComponentIsInSimulMode = true;
|
||||
}
|
||||
|
||||
if (cmpnt is IDevice)
|
||||
{
|
||||
devices.Add(cmpnt as IDevice); /// Only components that were initialized are added
|
||||
UiBridge.Bridge.OnActivity(null, cmpnt.Name); /// Info
|
||||
}
|
||||
|
||||
CurrentlyInitializedComponentName = cmpnt.Name;
|
||||
cmpnt.Initialize();
|
||||
cmpnt.StartChangeHandler(); /// Start handling parameter change events
|
||||
}
|
||||
|
||||
CurrentlyInitializedComponentName = "---";
|
||||
|
||||
///
|
||||
/// (1) Propagate debug levels from parents to children when necessary
|
||||
/// (2) Set drain valves DrainValve1, DrainValve2 and DrainValve3
|
||||
///
|
||||
foreach (var cmpnt in components)
|
||||
{
|
||||
if (cmpnt.Cfg is IChildComponentCfg && !string.IsNullOrEmpty(cmpnt.Cfg.ParentName) &&
|
||||
(cmpnt.Cfg.DebugLevel == DebugMode.Inherit || cmpnt.Cfg.DebugLevel == DebugMode.AutoDetect))
|
||||
{
|
||||
foreach (var par in components)
|
||||
{
|
||||
if (par.Cfg.Name.Equals(cmpnt.Cfg.ParentName)) { cmpnt.Cfg.DebugLevel = par.Cfg.DebugLevel; break; }
|
||||
}
|
||||
}
|
||||
|
||||
if (cmpnt is IScaleOrTank)
|
||||
{
|
||||
IScaleOrTank tank = cmpnt as IScaleOrTank;
|
||||
|
||||
if (tank == Tank1 && DrainValve1 == null)
|
||||
{
|
||||
DrainValve1 = tank.DrainValve;
|
||||
log.WarnFormat("InitializeComponents() ... Scale1={0} DrainValve1={1}",
|
||||
tank.Name, (DrainValve1 != null) ? DrainValve1.Name : "---");
|
||||
}
|
||||
else if (tank == Tank2 && DrainValve2 == null)
|
||||
{
|
||||
DrainValve2 = tank.DrainValve;
|
||||
log.WarnFormat("InitializeComponents() ... Scale2={0} DrainValve2={1}",
|
||||
tank.Name, (DrainValve2 != null) ? DrainValve2.Name : "---");
|
||||
}
|
||||
else if (tank == Tank3 && DrainValve3 == null)
|
||||
{
|
||||
DrainValve3 = tank.DrainValve;
|
||||
log.WarnFormat("InitializeComponents() ... Scale3={0} DrainValve3={1}",
|
||||
tank.Name, (DrainValve3 != null) ? DrainValve3.Name : "---");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (anyComponentIsInSimulMode)
|
||||
return inSimulMode.ToString();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop devices that were started by InitializeDevices().
|
||||
/// Works correctly also after exeption from InitializeDevices() as only ...
|
||||
/// ... correctly started devices were added in 'devices' list.
|
||||
/// </summary>
|
||||
public static void StopDevices()
|
||||
{
|
||||
for (int i = devices.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var dev = devices[i];
|
||||
UiBridge.Bridge.OnActivity(null, string.Format("1. {0}", dev.Name)); /// Info
|
||||
dev.StopDevice();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop devices that were started by InitializeDevices().
|
||||
/// Works correctly also after exeption from InitializeDevices() as only ...
|
||||
/// ... correctly started devices were added in 'devices' list.
|
||||
/// </summary>
|
||||
public static void StopDevices2()
|
||||
{
|
||||
for (int i = devices.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var dev = devices[i];
|
||||
UiBridge.Bridge.OnActivity(null, string.Format("2. {0}", dev.Name)); /// Info
|
||||
dev.StopDevice2();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks remote and local configuration DB paths and transitions for compatibility
|
||||
/// </summary>
|
||||
/// <returns>true when DB-s are compatible</returns>
|
||||
public static bool IsRemoteDBCompatible(out string message)
|
||||
{
|
||||
IList<Config.Entities.FeedingPath> remoteFeedingPaths;
|
||||
IList<Config.Entities.BenchPath> remoteBenchPaths;
|
||||
IList<Config.Entities.OutputPath> remoteOutputPaths;
|
||||
IList<Config.Entities.MetersPath> remoteMetersPaths;
|
||||
IList<Config.Entities.TransitionSequence> remoteTransitions;
|
||||
|
||||
try
|
||||
{
|
||||
ISession remoteSession = TBF.DB.CreateSession(Common.DBKind.RemoteConfig);
|
||||
remoteFeedingPaths = remoteSession.QueryOver<Config.Entities.FeedingPath>().List();
|
||||
remoteBenchPaths = remoteSession.QueryOver<Config.Entities.BenchPath>().List();
|
||||
remoteOutputPaths = remoteSession.QueryOver<Config.Entities.OutputPath>().List();
|
||||
remoteMetersPaths = remoteSession.QueryOver<Config.Entities.MetersPath>().List();
|
||||
remoteTransitions = remoteSession.QueryOver<Config.Entities.TransitionSequence>().List();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
message = "Cannot open remote database";
|
||||
return false;
|
||||
}
|
||||
|
||||
ISession localSession = TBF.DB.CreateSession(Common.DBKind.Config);
|
||||
var localFeedingPaths = localSession.QueryOver<Config.Entities.FeedingPath>().List();
|
||||
var localBenchPaths = localSession.QueryOver<Config.Entities.BenchPath>().List();
|
||||
var localOutputPaths = localSession.QueryOver<Config.Entities.OutputPath>().List();
|
||||
var localMetersPaths = localSession.QueryOver<Config.Entities.MetersPath>().List();
|
||||
var localTransitions = localSession.QueryOver<Config.Entities.TransitionSequence>().List();
|
||||
|
||||
string subMsg;
|
||||
if (!IsCompatible(localFeedingPaths, remoteFeedingPaths, out subMsg))
|
||||
{
|
||||
message = string.Format("Feeding: {0}", subMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsCompatible(localBenchPaths, remoteBenchPaths, out subMsg))
|
||||
{
|
||||
message = string.Format("Bench: {0}", subMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsCompatible(localOutputPaths, remoteOutputPaths, out subMsg))
|
||||
{
|
||||
message = string.Format("Output: {0}", subMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsCompatible(localMetersPaths, remoteMetersPaths, out subMsg))
|
||||
{
|
||||
message = string.Format("Sensors: {0}", subMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsCompatible(localTransitions, remoteTransitions, out subMsg))
|
||||
{
|
||||
message = string.Format("Transitions: {0}", subMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
message = string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="T">FeedingPath, BenchPath, OutputPath, MetersPath or TransitionSequence</typeparam>
|
||||
/// <param name="localList">Local list of paths or transitions</param>
|
||||
/// <param name="remoteList">Remote list of paths or transitions</param>
|
||||
/// <param name="message">Message specifying a cause of incompatibility</param>
|
||||
/// <returns>true when lists are compatible, otherwise false</returns>
|
||||
private static bool IsCompatible<T>(IList<T> localList, IList<T> remoteList, out string message)
|
||||
{
|
||||
message = "Local list is empty";
|
||||
if ((localList == null) || (localList.Count == 0) || !(localList[0] is IHasName)) return false;
|
||||
|
||||
message = "Remote list is empty";
|
||||
if ((remoteList == null) || (remoteList.Count == 0) || !(remoteList[0] is IHasName)) return false;
|
||||
|
||||
///
|
||||
/// Each path or transition on a remote list must exist on a local list
|
||||
///
|
||||
foreach (var rItem in remoteList)
|
||||
{
|
||||
bool exists = false;
|
||||
foreach (var lItem in localList)
|
||||
{
|
||||
if ((rItem as IHasName).Name == (lItem as IHasName).Name)
|
||||
{
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!exists)
|
||||
{
|
||||
message = string.Format("Remote item {0} does not exist on a local list", (rItem as IHasName).Name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
message = string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Start the state machine
|
||||
/// </summary>
|
||||
public static void Start()
|
||||
{
|
||||
if (MachineState != MachineState.StartingUp)
|
||||
{
|
||||
/// Unexpected MachineState (not MachineState.StartingUp) --> prevent worker thread from starting
|
||||
///
|
||||
MachineState = MachineState.FailedToStart;
|
||||
return;
|
||||
}
|
||||
|
||||
workerThread = new Thread(Worker);
|
||||
workerThread.CurrentCulture = Thread.CurrentThread.CurrentCulture;
|
||||
workerThread.CurrentUICulture = Thread.CurrentThread.CurrentUICulture;
|
||||
workerThread.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads all paths and transitions from the DB.
|
||||
/// Updates StatMachine.feedingPaths ... StatMachine.meterPaths, StatMachine.TransitionSequences
|
||||
/// </summary>
|
||||
public static void LoadPathsAndTransitions(ISession session)
|
||||
{
|
||||
feedingPaths = session.QueryOver<Config.Entities.FeedingPath>().OrderBy(x => x.ItemNr).Asc.List();
|
||||
benchPaths = session.QueryOver<Config.Entities.BenchPath>().OrderBy(x => x.ItemNr).Asc.List();
|
||||
outputPaths = session.QueryOver<Config.Entities.OutputPath>().OrderBy(x => x.ItemNr).Asc.List();
|
||||
metersPaths = session.QueryOver<Config.Entities.MetersPath>().OrderBy(x => x.ItemNr).Asc.List();
|
||||
TransitionSequences = session.QueryOver<TransitionSequence>().OrderBy(x => x.ItemNr).Asc.List();
|
||||
TransitionSteps = session.QueryOver<TransitionStep>().OrderBy(x => x.ItemNr).Asc.List();
|
||||
#if HEAT_METERS
|
||||
heatMetersPaths = session.QueryOver<Config.Entities.HeatMetersPath>().OrderBy(x => x.ItemNr).Asc.List();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads selected procedure from the DB.
|
||||
/// Updates StateMachine.Procedure and StateMachine.Tests
|
||||
/// </summary>
|
||||
public static bool LoadProcedure(ISession session, string procedureName, bool isRemote, IList<string> autoTests = null)
|
||||
{
|
||||
Procedure = null;
|
||||
|
||||
IList<Procedure> selectedProcs = session.QueryOver<Procedure>()
|
||||
.Where(x => (x.ProcedureState == Common.ProcedureState.Active))
|
||||
.And(x => (x.Name == procedureName))
|
||||
.List();
|
||||
if (selectedProcs.Count == 1)
|
||||
{
|
||||
IsRemoteProcedure = isRemote;
|
||||
Procedure = selectedProcs[0];
|
||||
TestInstances = selectedProcs[0].UpdateTestInstances(LoopStartNames, LoopEndNames, autoTests);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void LoadProcedureParams(Procedure procedure)
|
||||
{
|
||||
foreach (var cmpnt in components)
|
||||
{
|
||||
cmpnt.Cfg.LoadProcedureParamsFromDB(procedure);
|
||||
}
|
||||
}
|
||||
|
||||
public static void LoadTestParams(Test test)
|
||||
{
|
||||
foreach (var cmpnt in components)
|
||||
{
|
||||
cmpnt.Cfg.LoadTestParamsFromDB(test);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called from the sequence to update paths based on the selected test
|
||||
/// </summary>
|
||||
/// <param name="test">Selected test</param>
|
||||
/// <param name="pfeed"></param>
|
||||
/// <param name="pben"></param>
|
||||
/// <param name="pout"></param>
|
||||
/// <param name="pmtrs"></param>
|
||||
/// <param name="transitionBefore">Transition sequence entity</param>
|
||||
/// <param name="transitionAfter">Transition sequence entity</param>
|
||||
/// <param name="errorMsg">Error message in case of incorrect configuration</param>
|
||||
/// <returns>true when loaded configuration is correct (all four paths are defined !=null, etc.)</returns>
|
||||
public static bool GetPaths(Test test,
|
||||
bool heatMetersPathRequired,
|
||||
out FeedingPath pfeed,
|
||||
out BenchPath pben,
|
||||
out OutputPath pout,
|
||||
out MetersPath pmtrs,
|
||||
out HeatMetersPath phmtrs,
|
||||
out TransitionSequence transitionBefore,
|
||||
out TransitionSequence transitionBetween,
|
||||
out TransitionSequence transitionAfter,
|
||||
out string errorMsg)
|
||||
{
|
||||
ITestMethod tm = TbfComponents.FindComponent(test.Method) as ITestMethod;
|
||||
bool isHydroTest = (tm != null) ? tm.DoTransitions() : false;
|
||||
|
||||
pfeed = null;
|
||||
pben = null;
|
||||
pout = null;
|
||||
phmtrs = null;
|
||||
transitionBefore = null;
|
||||
transitionBetween = null;
|
||||
transitionAfter = null;
|
||||
|
||||
foreach (var path in feedingPaths)
|
||||
{
|
||||
if (test.FeedingPath == path.Name) { pfeed = new FeedingPath(path, components); break; }
|
||||
}
|
||||
|
||||
foreach (var path in benchPaths)
|
||||
{
|
||||
if (test.BenchPath == path.Name) { pben = new BenchPath(path, components); break; }
|
||||
}
|
||||
|
||||
foreach (var path in outputPaths)
|
||||
{
|
||||
if (test.OutputPath == path.Name) { pout = new OutputPath(path, components); break; }
|
||||
}
|
||||
|
||||
pmtrs = GetMetersPath(test);
|
||||
|
||||
if (isHydroTest && (pfeed == null)) errorMsg = Strings.Cannot_load_feeding_path;
|
||||
else if (isHydroTest && (pben == null)) errorMsg = Strings.Cannot_load_bench_path;
|
||||
else if (isHydroTest && (pout == null)) errorMsg = Strings.Cannot_load_output_path;
|
||||
else if (pmtrs == null) errorMsg = Strings.Cannot_load_sensor_path;
|
||||
else errorMsg = string.Empty;
|
||||
if ((isHydroTest && (pfeed == null || pben == null || pout == null)) || (pmtrs == null))
|
||||
{
|
||||
log.ErrorFormat("GetPaths({0},...) returns false, wrong configuration", test.Name);
|
||||
return false;
|
||||
}
|
||||
|
||||
#if HEAT_METERS
|
||||
foreach (var path in heatMetersPaths)
|
||||
{
|
||||
if (test.HeatMetersPath == path.Name) { phmtrs = new HeatMetersPath(path, components); break; }
|
||||
}
|
||||
#endif
|
||||
if (heatMetersPathRequired && phmtrs == null)
|
||||
{
|
||||
log.ErrorFormat("GetPaths({0},...) returns false, missing heat meters sensors", test.Name);
|
||||
errorMsg = "Cannot load heat meters sensors";
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var tr in TransitionSequences)
|
||||
{
|
||||
if (tr.Name == test.TransBefore) transitionBefore = tr;
|
||||
if (tr.Name == test.TransBetween) transitionBetween = tr;
|
||||
if (tr.Name == test.TransAfter) transitionAfter = tr;
|
||||
}
|
||||
|
||||
if (isHydroTest && pout.Scale == null)
|
||||
{
|
||||
log.ErrorFormat("GetPaths({0},...) returns false, missing scales in output path {1}", test.Name, test.OutputPath);
|
||||
errorMsg = string.Format("No balance specified in path {0}", test.OutputPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Config.Formulas.RealDensity() < 500.0f || Config.Formulas.RealDensity() > 2000.0f)
|
||||
{
|
||||
log.ErrorFormat("GetPaths({0},...) returns false, density was not specified", test.Name);
|
||||
errorMsg = string.Format("Density was not specified");
|
||||
return false;
|
||||
}
|
||||
|
||||
log.WarnFormat("GetPaths({0},...) returns true, paths loaded OK, isHydroTest={1}", test.Name, isHydroTest);
|
||||
errorMsg = string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Updates paths based on the selected test
|
||||
/// </summary>
|
||||
public static MetersPath GetMetersPath(Test test)
|
||||
{
|
||||
MetersPath pmtrs = null;
|
||||
foreach (var path in metersPaths)
|
||||
{
|
||||
if (test.MetersPath == path.Name) { pmtrs = new MetersPath(path, components); break; }
|
||||
}
|
||||
if (pmtrs != null)
|
||||
{
|
||||
int count = Math.Min(TBF.Data.WMsCount, pmtrs.RegisterReaders.Length);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if ((pmtrs.RegisterReaders[i] != null) &&
|
||||
(pmtrs.RegisterReaders[i].Cfg.DebugLevel == DebugMode.DetectedOff))
|
||||
{
|
||||
pmtrs.RegisterReaders[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return pmtrs;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This is and example sequence of RunDeviceBefore() / RunOperations() / RunDeviceAfter() calls
|
||||
* as they are executed during normal run from the progran start to the end.
|
||||
*
|
||||
foreach (var device in devices) device.RunDeviceBefore(); . . . . . . in StateMachine.Worker()
|
||||
State.Create(...).AddOperation(...).AddOperation(...).EnterState() . . in the sequence in Execute(...)
|
||||
|
||||
foreach (var device in devices) device.RunDeviceAfter(); . . . . . . . in WaitRunDevsRunOps()
|
||||
WaitNextTick() (may throw QuitStateMachineException) . . . . . . . . in WaitRunDevsRunOps()
|
||||
foreach (var device in devices) device.RunDeviceBefore(); . . . . . . in WaitRunDevsRunOps()
|
||||
IList<Event> events = State.RunOperations(); . . . . . . . . . . . . . in WaitRunDevsRunOps()
|
||||
|
||||
foreach (var device in devices) device.RunDeviceAfter(); . . . . . . . in WaitRunDevsRunOps()
|
||||
WaitNextTick() (may throw QuitStateMachineException) . . . . . . . . in WaitRunDevsRunOps()
|
||||
foreach (var device in devices) device.RunDeviceBefore(); . . . . . . in WaitRunDevsRunOps()
|
||||
IList<Event> events = State.RunOperations(); . . . . . . . . . . . . . in WaitRunDevsRunOps()
|
||||
|
||||
State.Create(...).AddOperation(...).AddOperation(...).EnterState() . . in the sequence in Execute(...)
|
||||
|
||||
foreach (var device in devices) device.RunDeviceAfter(); . . . . . . . in WaitRunDevsRunOps()
|
||||
WaitNextTick() (may throw QuitStateMachineException) . . . . . . . . in WaitRunDevsRunOps()
|
||||
foreach (var device in devices) device.RunDeviceBefore(); . . . . . . in WaitRunDevsRunOps()
|
||||
IList<Event> events = State.RunOperations(); . . . . . . . . . . . . . in WaitRunDevsRunOps()
|
||||
|
||||
foreach (var device in devices) device.RunDeviceAfter(); . . . . . . . in WaitRunDevsRunOps()
|
||||
WaitNextTick() (assume QuitStateMachineException thrown) . . . . . . in WaitRunDevsRunOps()
|
||||
State.StopOperations(); . . . . . . . . . . . . . . . . . . . . . . . in StateMachine.Worker() catch()
|
||||
foreach (var device in devices) device.StopDevice(); . . . . . . . . . in StateMachine.Worker() catch()
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Worker thread: calls Start(), Run() and Stop() methods of operations.
|
||||
/// It uses 'currentState', 'nextState' and 'quitStateMachine' static fields.
|
||||
/// </summary>
|
||||
static void Worker()
|
||||
{
|
||||
MachineState = MachineState.Running;
|
||||
startDateTime = DateTime.Now;
|
||||
CycleStartTimeStamp = startDateTime; /// Do not leave CycleStartTimeStamp uninitialized
|
||||
wlog.InfoFormat(" currentTime = {0}s startDateTime = {1}", currentTimeSec.ToString(), startDateTime.ToString());
|
||||
|
||||
/// Run all devices for the first time
|
||||
foreach (var device in devices) device.RunDeviceBefore();
|
||||
|
||||
SequenceBase.ReferenceFlowmetersCount = SequenceBase.FlowMeters.Count;
|
||||
SequenceBase.CalibratedLtrPerRefPulse = new double[SequenceBase.ReferenceFlowmetersCount];
|
||||
foreach (var flowmtr in SequenceBase.FlowMeters)
|
||||
{
|
||||
int ix = flowmtr.Idx1;
|
||||
if (ix > 0 && ix <= SequenceBase.ReferenceFlowmetersCount)
|
||||
{
|
||||
SequenceBase.CalibratedLtrPerRefPulse[ix - 1] = flowmtr.NominalFlow / 7200.0;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Run the main sequence
|
||||
///
|
||||
(new Sequences.MainSeq()).Execute();
|
||||
|
||||
StateMachine.MachineState = MachineState.Off; /// Update StateMachine.MachineState
|
||||
TBF.UiBridge.Bridge.Bench2UI(TBF.UiBridge.ButtonsEtc.Shutdown); /// Let UI know the state machine is shut down now
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Do stuff that is repeated in the state execution loops most often
|
||||
/// </summary>
|
||||
/// <returns>List of Event-s returned from the state operations, null == quit</returns>
|
||||
public static IList<Event> WaitRunDevsRunOps(bool lastTime = false)
|
||||
{
|
||||
Thread.Sleep(100); /// Insert 1/10 sec.delay between RunOperations() and RunDeviceAfter()
|
||||
|
||||
for (int i = devices.Count - 1; i >= 0; i-- )
|
||||
{
|
||||
devices[i].RunDeviceAfter();
|
||||
}
|
||||
|
||||
WaitNextTick();
|
||||
|
||||
if (lastTime)
|
||||
{
|
||||
///
|
||||
/// Executed when the state machine is being stopped
|
||||
///
|
||||
State.StopOperations();
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (var device in devices)
|
||||
{
|
||||
device.RunDeviceBefore();
|
||||
}
|
||||
|
||||
Thread.Sleep(100); /// Insert 1/10 sec.delay between RunDeviceBefore() and RunOperations()
|
||||
|
||||
return State.RunOperations(); /// Returns events from all running operations
|
||||
/// This is followed by a state change in the sequence
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Wait time period - synchronize
|
||||
/// </summary>
|
||||
/// <returns>true when interrupted by 'quitStateMachine', otherwise false</returns>
|
||||
public static void WaitNextTick()
|
||||
{
|
||||
int currentTimeSecFromClock = Convert.ToInt32(Math.Round((DateTime.Now - startDateTime).TotalSeconds - 0.2));
|
||||
|
||||
currentTimeSec = Math.Max(currentTimeSec, currentTimeSecFromClock) + Period;
|
||||
|
||||
DateTime nextTickDateTime = startDateTime + TimeSpan.FromSeconds(currentTimeSec);
|
||||
|
||||
while (DateTime.Now < nextTickDateTime)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -30,6 +30,13 @@ namespace TBF.Rig.Sequences
|
||||
public static int LinesCount { get { return TBF.Data.WMsCount / TBF.Data.LineSize; } }
|
||||
public static int LineSize { get { return TBF.Data.LineSize; } }
|
||||
public static int CompoundWMsCount { get { return TBF.Data.CompoundWMsCount; } }
|
||||
///
|
||||
public static Unit VolumeUnit { get { return BenchInfo != null ? BenchInfo.VolumeUnit : Unit.l; } }
|
||||
public static Unit FlowUnit { get { return BenchInfo != null ? BenchInfo.FlowUnit : Unit.m3ph; } }
|
||||
public static Unit MassUnit { get { return BenchInfo != null ? BenchInfo.MassUnit : Unit.kg; } }
|
||||
public static Unit TempUnit { get { return BenchInfo != null ? BenchInfo.TempUnit : Unit.C; } }
|
||||
public static Unit PressUnit { get { return BenchInfo != null ? BenchInfo.PressUnit : Unit.bar; } }
|
||||
public static Unit LengthUnit { get { return BenchInfo != null ? BenchInfo.LengthUnit : Unit.mm; } }
|
||||
|
||||
///
|
||||
/// Procedure related state variables
|
||||
|
||||
Loading…
Reference in New Issue
Block a user