2015-04-15 18:32:56 +00:00
|
|
|
|
///
|
2022-11-02 09:14:30 +00:00
|
|
|
|
/// Copyright (c) 2013-2022 Sensus Slovensko a.s.
|
2015-04-15 18:32:56 +00:00
|
|
|
|
///
|
|
|
|
|
|
using System;
|
2014-07-28 07:54:35 +00:00
|
|
|
|
using System.Text;
|
|
|
|
|
|
using System.Threading;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using log4net;
|
|
|
|
|
|
using NHibernate;
|
2021-09-23 09:46:40 +00:00
|
|
|
|
using Common;
|
2015-12-04 16:17:20 +00:00
|
|
|
|
using Config.Entities;
|
2021-10-26 09:23:57 +00:00
|
|
|
|
using TBF.Rig.Generic;
|
|
|
|
|
|
using TBF.Rig.GenericDevices;
|
|
|
|
|
|
using TBF.Rig.Sequences;
|
2016-12-12 16:25:12 +00:00
|
|
|
|
using Dirichlet.Numerics;
|
2017-05-03 20:17:20 +00:00
|
|
|
|
using TBF.Resources;
|
2014-07-28 07:54:35 +00:00
|
|
|
|
|
2021-10-26 09:23:57 +00:00
|
|
|
|
namespace TBF.Rig
|
2014-07-28 07:54:35 +00:00
|
|
|
|
{
|
2019-03-04 12:14:13 +00:00
|
|
|
|
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
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-07-28 07:54:35 +00:00
|
|
|
|
|
|
|
|
|
|
/// <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 devices and components
|
|
|
|
|
|
static IList<IComponent> components; /// list of all components
|
|
|
|
|
|
static IList<IDevice> devices; /// list of devices
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Bench paths
|
2015-12-04 16:17:20 +00:00
|
|
|
|
static IList<Config.Entities.FeedingPath> feedingPaths;
|
|
|
|
|
|
static IList<Config.Entities.BenchPath> benchPaths;
|
|
|
|
|
|
static IList<Config.Entities.OutputPath> outputPaths;
|
|
|
|
|
|
static IList<Config.Entities.MetersPath> metersPaths;
|
2016-06-28 16:04:52 +00:00
|
|
|
|
static IList<Config.Entities.HeatMetersPath> heatMetersPaths;
|
|
|
|
|
|
public static IList<Config.Entities.TransitionSequence> TransitionSequences;
|
2018-06-15 07:38:18 +00:00
|
|
|
|
public static IList<Config.Entities.TransitionStep> TransitionSteps;
|
2014-07-28 07:54:35 +00:00
|
|
|
|
|
|
|
|
|
|
/// Public components
|
|
|
|
|
|
public static Elde.ControlBoardDev ControlBoard;
|
|
|
|
|
|
public static GenericDevices.IAmbient Ambient;
|
2019-02-12 08:00:02 +00:00
|
|
|
|
public static IOperation BenchWaitingOp;
|
|
|
|
|
|
public static IOperation BenchErrorOp;
|
|
|
|
|
|
public static GenericDevices.IScaleOrTank Tank1;
|
2018-10-22 16:14:47 +00:00
|
|
|
|
public static GenericDevices.IScaleOrTank Tank2;
|
|
|
|
|
|
public static GenericDevices.IScaleOrTank Tank3;
|
2017-03-28 10:38:47 +00:00
|
|
|
|
public static GenericDevices.IValve DrainValve1;
|
|
|
|
|
|
public static GenericDevices.IValve DrainValve2;
|
|
|
|
|
|
public static GenericDevices.IValve DrainValve3;
|
2015-06-29 09:26:08 +00:00
|
|
|
|
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
|
2014-07-28 07:54:35 +00:00
|
|
|
|
|
2021-01-21 14:00:55 +00:00
|
|
|
|
public static IList<string> LoopStartNames;
|
|
|
|
|
|
public static IList<string> LoopEndNames;
|
2016-04-07 13:37:49 +00:00
|
|
|
|
|
|
|
|
|
|
/// Time and synchronization
|
2019-07-24 13:38:49 +00:00
|
|
|
|
#if TURA_IPERL || TURA_IPERL_NEW
|
2015-08-19 11:00:00 +00:00
|
|
|
|
public const int Period = 2; /// State machine period in sec.
|
2016-04-07 13:37:49 +00:00
|
|
|
|
#else
|
|
|
|
|
|
public const int Period = 1; /// State machine period in sec.
|
2015-08-19 11:00:00 +00:00
|
|
|
|
#endif
|
2015-09-17 16:14:49 +00:00
|
|
|
|
|
2014-07-28 07:54:35 +00:00
|
|
|
|
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
|
|
|
|
|
|
|
2019-03-04 12:14:13 +00:00
|
|
|
|
public static MachineState MachineState; /// see enum MachineState for explanation
|
2014-07-28 07:54:35 +00:00
|
|
|
|
|
|
|
|
|
|
/// Worker thread and database session
|
|
|
|
|
|
static Thread workerThread;
|
|
|
|
|
|
|
|
|
|
|
|
static IList<State> states; /// list of states
|
|
|
|
|
|
|
2016-03-02 19:02:30 +00:00
|
|
|
|
public static DateTime CycleStartTimeStamp;
|
2014-07-28 07:54:35 +00:00
|
|
|
|
|
|
|
|
|
|
public static IList<IValve> DefaultValvesOpen
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
return GenericDevices.ValveBase.Merge(
|
|
|
|
|
|
Utils.ValvesOpen((feedingPaths != null && feedingPaths.Count > 0) ? feedingPaths[0] : null),
|
2017-03-28 21:47:32 +00:00
|
|
|
|
Utils.ValvesOpen((benchPaths != null && benchPaths.Count > 0) ? benchPaths[0] : null),
|
|
|
|
|
|
Utils.ValvesOpen((outputPaths != null && outputPaths.Count > 0) ? outputPaths[0] : null)
|
2014-07-28 07:54:35 +00:00
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static IList<IValve> DefaultValvesClose
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
return GenericDevices.ValveBase.Merge(
|
|
|
|
|
|
Utils.ValvesClose((feedingPaths != null && feedingPaths.Count > 0) ? feedingPaths[0] : null),
|
2017-03-28 21:47:32 +00:00
|
|
|
|
Utils.ValvesClose((benchPaths != null && benchPaths.Count > 0) ? benchPaths[0] : null),
|
|
|
|
|
|
Utils.ValvesClose((outputPaths != null && outputPaths.Count > 0) ? outputPaths[0] : null)
|
2014-07-28 07:54:35 +00:00
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Loaded by LoadProcedure() or IOperation LoadProcedureOp(...)
|
|
|
|
|
|
/// </summary>
|
2021-01-21 14:00:55 +00:00
|
|
|
|
public static Procedure Procedure; /// Procedure
|
|
|
|
|
|
public static TestInstance[] TestInstances; /// Tests
|
2020-02-06 14:39:38 +00:00
|
|
|
|
public static bool IsRemoteProcedure;
|
2014-07-28 07:54:35 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// 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>
|
2018-10-01 09:19:57 +00:00
|
|
|
|
public static int Time
|
|
|
|
|
|
{
|
|
|
|
|
|
get { return currentTimeSec; }
|
|
|
|
|
|
set { currentTimeSec = value; }
|
|
|
|
|
|
}
|
2014-07-28 07:54:35 +00:00
|
|
|
|
|
|
|
|
|
|
/// <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)
|
2020-10-22 07:42:19 +00:00
|
|
|
|
#elif FUZHOU_150 || MUNICH
|
2014-07-28 07:54:35 +00:00
|
|
|
|
public static void InitializeBoardEtc(ControlComponent3Munich.UserControl1 ctrlBrdComponent)
|
2018-09-13 11:05:15 +00:00
|
|
|
|
#elif FUZHOU_300
|
2014-09-05 12:17:12 +00:00
|
|
|
|
public static void InitializeBoardEtc(ControlComponent3F300.UserControl1 ctrlBrdComponent)
|
2020-10-22 07:42:19 +00:00
|
|
|
|
#elif BERLIN || GELSENWASSER || GENESIS || IZRAEL_200 || PETERSBURG_200 || PUCHONG_200 || SLM_150
|
2017-01-24 14:05:30 +00:00
|
|
|
|
public static void InitializeBoardEtc(ControlComponent_Izrael2014.UserControl1 ctrlBrdComponent)
|
2015-09-17 16:14:49 +00:00
|
|
|
|
#else /// all newer benches
|
2023-04-18 12:01:23 +00:00
|
|
|
|
public static void InitializeBoardEtc(ISession session, ControlComponent_Torino2015.UserControl1 ctrlBrdComponent)
|
2014-07-28 07:54:35 +00:00
|
|
|
|
#endif
|
2016-12-14 10:09:23 +00:00
|
|
|
|
{
|
2014-07-28 07:54:35 +00:00
|
|
|
|
/// Load the list of components (entities) from the database.
|
|
|
|
|
|
/// Then create the components (derived from IComponent).
|
2023-03-28 11:03:38 +00:00
|
|
|
|
components = Rig.TbfComponents.LoadComponentsFromDB(session);
|
2014-07-28 07:54:35 +00:00
|
|
|
|
MasterValves = GenericDevices.ValveBase.MasterValves(components);
|
|
|
|
|
|
CoupledValves = GenericDevices.ValveBase.CoupledValves(components);
|
2015-06-29 09:26:08 +00:00
|
|
|
|
ExtendedValves = GenericDevices.ValveBase.ExtendedValves(components);
|
2014-07-28 07:54:35 +00:00
|
|
|
|
|
|
|
|
|
|
/// Find all balances (to initialize tank capacities in the control board)
|
|
|
|
|
|
/// Find the control board
|
2018-10-22 16:14:47 +00:00
|
|
|
|
IList<IScaleOrTank> tanks = new List<IScaleOrTank>();
|
2020-02-04 07:10:19 +00:00
|
|
|
|
ProcessData.IperlHeads = new List<TestMethods.iPerlCommunication.iPerlHead.IperlHead>();
|
2014-09-06 15:34:26 +00:00
|
|
|
|
SequenceBase.FlowMeters = new List<IFlowMeter>();
|
2022-11-02 09:14:30 +00:00
|
|
|
|
SequenceBase.RegVPositions = new List<RegValvePosition>();
|
2014-09-08 11:16:31 +00:00
|
|
|
|
SequenceBase.PumpsWithFM = new List<IPumpFM>();
|
2014-07-28 07:54:35 +00:00
|
|
|
|
SequenceBase.WaterMeters = new List<IWaterMeter>();
|
|
|
|
|
|
SequenceBase.Cameras = new List<ICamera>();
|
2021-01-21 14:00:55 +00:00
|
|
|
|
LoopStartNames = new List<string>();
|
|
|
|
|
|
LoopEndNames = new List<string>();
|
2016-12-12 16:25:12 +00:00
|
|
|
|
UInt128 valvesToInvert = 0;
|
2014-07-28 07:54:35 +00:00
|
|
|
|
foreach (var cmpnt in components)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (cmpnt is Elde.ControlBoardDev) ControlBoard = cmpnt as Elde.ControlBoardDev;
|
2022-12-06 09:29:55 +00:00
|
|
|
|
|
|
|
|
|
|
if (cmpnt is IBenchInfo)
|
|
|
|
|
|
{
|
|
|
|
|
|
ProcessData.BenchInfo = cmpnt as IBenchInfo;
|
|
|
|
|
|
if (cmpnt is TBF.Rig.DataContainers.BenchInfo.iPerl.Component)
|
|
|
|
|
|
{
|
|
|
|
|
|
Results.Utils.MaxTestIndex = (cmpnt as TBF.Rig.DataContainers.BenchInfo.iPerl.Component).MaxTestIndex;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (cmpnt is IErrorFlags) ProcessData.ErrorFlagsComp = cmpnt as IErrorFlags;
|
2020-05-05 13:42:15 +00:00
|
|
|
|
if (cmpnt is IStatisticsMonitoring) ProcessData.StatisticsMonitoringComp = cmpnt as IStatisticsMonitoring;
|
|
|
|
|
|
|
2018-09-11 11:39:33 +00:00
|
|
|
|
if ((cmpnt is Output.DB.SensusOracle.Database) && (ProcessData.OracleDB == null))
|
|
|
|
|
|
{
|
2021-07-28 09:40:45 +00:00
|
|
|
|
/// The 1st Oracle database component found --> store the reference
|
2018-09-11 11:39:33 +00:00
|
|
|
|
ProcessData.OracleDB = cmpnt as Output.DB.SensusOracle.Database;
|
|
|
|
|
|
}
|
2021-07-28 09:40:45 +00:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-02-04 07:10:19 +00:00
|
|
|
|
if (cmpnt is TestMethods.iPerlCommunication.iPerlHead.IperlHead)
|
|
|
|
|
|
{
|
|
|
|
|
|
ProcessData.IperlHeads.Add(cmpnt as TestMethods.iPerlCommunication.iPerlHead.IperlHead);
|
|
|
|
|
|
}
|
2014-09-06 15:34:26 +00:00
|
|
|
|
if (cmpnt is IFlowMeter) SequenceBase.FlowMeters.Add(cmpnt as IFlowMeter);
|
2021-10-26 09:23:57 +00:00
|
|
|
|
if ((cmpnt is IRegValve) && !(cmpnt is Rig.Elde.RegulValveTandem.RegulValveTandem))
|
2015-02-03 09:53:02 +00:00
|
|
|
|
{
|
2022-11-02 09:14:30 +00:00
|
|
|
|
SequenceBase.RegVPositions.Add(new RegValvePosition(cmpnt as IRegValve));
|
2015-02-03 09:53:02 +00:00
|
|
|
|
}
|
2018-07-12 13:59:09 +00:00
|
|
|
|
if (cmpnt is IPumpFM && !(cmpnt is Elde.PumpTandem.Pump))
|
|
|
|
|
|
{
|
|
|
|
|
|
SequenceBase.PumpsWithFM.Add(cmpnt as IPumpFM);
|
|
|
|
|
|
}
|
2014-09-08 11:16:31 +00:00
|
|
|
|
if (cmpnt is IWaterMeter) SequenceBase.WaterMeters.Add(cmpnt as IWaterMeter);
|
2014-07-28 07:54:35 +00:00
|
|
|
|
if (cmpnt is ICamera) SequenceBase.Cameras.Add(cmpnt as ICamera);
|
|
|
|
|
|
if (cmpnt is GenericDevices.IAmbient) Ambient = cmpnt as GenericDevices.IAmbient;
|
2019-02-12 08:00:02 +00:00
|
|
|
|
|
|
|
|
|
|
if (cmpnt is GenericDevices.IParallelOutput && cmpnt.Name.ToLower().Contains("tower"))
|
|
|
|
|
|
{
|
|
|
|
|
|
BenchWaitingOp = (cmpnt as GenericDevices.IParallelOutput).ShowBenchWaitingOp();
|
|
|
|
|
|
BenchErrorOp = (cmpnt as GenericDevices.IParallelOutput).ShowBenchErrorOp();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-21 14:00:55 +00:00
|
|
|
|
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);
|
|
|
|
|
|
|
2018-10-22 16:14:47 +00:00
|
|
|
|
if (cmpnt is IScaleOrTank)
|
2014-07-28 07:54:35 +00:00
|
|
|
|
{
|
2018-10-22 16:14:47 +00:00
|
|
|
|
IScaleOrTank tank = cmpnt as IScaleOrTank;
|
|
|
|
|
|
tanks.Add(tank);
|
2021-04-12 10:00:05 +00:00
|
|
|
|
if (Tank1 == null) Tank1 = tank;
|
|
|
|
|
|
else if (Tank2 == null) Tank2 = tank;
|
|
|
|
|
|
else if (Tank3 == null) Tank3 = tank;
|
2014-08-12 12:52:42 +00:00
|
|
|
|
}
|
2014-09-06 11:29:04 +00:00
|
|
|
|
|
|
|
|
|
|
Elde.Valve.Valve eldeValve = (cmpnt as Elde.Valve.Valve);
|
|
|
|
|
|
if ((eldeValve != null) && eldeValve.Inverted) valvesToInvert |= eldeValve.Mask;
|
2014-07-28 07:54:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Pre-initialize the control board (= buffer the arguments ctrlBrdComponent, tankCapacities)
|
2021-04-12 10:00:05 +00:00
|
|
|
|
if (ControlBoard != null)
|
2016-02-14 18:02:07 +00:00
|
|
|
|
{
|
2021-04-12 10:00:05 +00:00
|
|
|
|
/// 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);
|
2016-02-14 18:02:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new Exception("Control board component is missing");
|
|
|
|
|
|
}
|
2014-07-28 07:54:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-01-16 12:34:40 +00:00
|
|
|
|
|
2021-04-07 07:57:42 +00:00
|
|
|
|
public static string CurrentlyInitializedComponentName;
|
2017-01-16 12:34:40 +00:00
|
|
|
|
///
|
2021-04-12 10:00:05 +00:00
|
|
|
|
public static string InitializeComponents()
|
2014-07-28 07:54:35 +00:00
|
|
|
|
{
|
2021-04-07 07:57:42 +00:00
|
|
|
|
CurrentlyInitializedComponentName = "-";
|
2017-01-16 12:34:40 +00:00
|
|
|
|
|
|
|
|
|
|
bool anyComponentIsInSimulMode = false;
|
2015-08-24 14:04:31 +00:00
|
|
|
|
StringBuilder inSimulMode = new StringBuilder();
|
|
|
|
|
|
|
2014-07-28 07:54:35 +00:00
|
|
|
|
/// Add all devices to the state machine and initialize them
|
|
|
|
|
|
foreach (var cmpnt in components)
|
|
|
|
|
|
{
|
2015-12-04 16:17:20 +00:00
|
|
|
|
if (cmpnt.DebugLevel == DebugMode.Simulate)
|
2015-08-24 14:04:31 +00:00
|
|
|
|
{
|
|
|
|
|
|
inSimulMode.AppendFormat("{0}{1}", anyComponentIsInSimulMode ? ", " : "", cmpnt.Name);
|
|
|
|
|
|
anyComponentIsInSimulMode = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-04-07 07:57:42 +00:00
|
|
|
|
if (cmpnt is IDevice)
|
|
|
|
|
|
{
|
|
|
|
|
|
devices.Add(cmpnt as IDevice); /// Only components that were initialized are added
|
|
|
|
|
|
UiBridge.Bridge.OnActivity(null, cmpnt.Name); /// Info
|
|
|
|
|
|
}
|
2017-08-18 09:50:28 +00:00
|
|
|
|
|
2021-04-07 07:57:42 +00:00
|
|
|
|
CurrentlyInitializedComponentName = cmpnt.Name;
|
|
|
|
|
|
cmpnt.Initialize();
|
2021-04-12 10:00:05 +00:00
|
|
|
|
cmpnt.StartChangeHandler(); /// Start handling parameter change events
|
|
|
|
|
|
}
|
2017-01-16 12:34:40 +00:00
|
|
|
|
|
2021-04-07 07:57:42 +00:00
|
|
|
|
CurrentlyInitializedComponentName = "---";
|
2014-07-28 07:54:35 +00:00
|
|
|
|
|
2017-03-28 10:38:47 +00:00
|
|
|
|
///
|
|
|
|
|
|
/// (1) Propagate debug levels from parents to children when necessary
|
|
|
|
|
|
/// (2) Set drain valves DrainValve1, DrainValve2 and DrainValve3
|
|
|
|
|
|
///
|
2014-07-28 07:54:35 +00:00
|
|
|
|
foreach (var cmpnt in components)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (cmpnt.Cfg is IChildComponentCfg && !string.IsNullOrEmpty(cmpnt.Cfg.ParentName) &&
|
2015-12-04 16:17:20 +00:00
|
|
|
|
(cmpnt.Cfg.DebugLevel == DebugMode.Inherit || cmpnt.Cfg.DebugLevel == DebugMode.AutoDetect))
|
2014-07-28 07:54:35 +00:00
|
|
|
|
{
|
|
|
|
|
|
foreach (var par in components)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (par.Cfg.Name.Equals(cmpnt.Cfg.ParentName)) { cmpnt.Cfg.DebugLevel = par.Cfg.DebugLevel; break; }
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2017-03-28 10:38:47 +00:00
|
|
|
|
|
2018-10-22 16:14:47 +00:00
|
|
|
|
if (cmpnt is IScaleOrTank)
|
2017-03-28 10:38:47 +00:00
|
|
|
|
{
|
2018-10-22 16:14:47 +00:00
|
|
|
|
IScaleOrTank tank = cmpnt as IScaleOrTank;
|
2017-03-28 10:38:47 +00:00
|
|
|
|
|
2021-04-12 10:00:05 +00:00
|
|
|
|
if (tank == Tank1 && DrainValve1 == null)
|
2017-03-28 10:38:47 +00:00
|
|
|
|
{
|
2018-10-22 16:14:47 +00:00
|
|
|
|
DrainValve1 = tank.DrainValve;
|
2021-04-12 10:00:05 +00:00
|
|
|
|
log.WarnFormat("InitializeComponents() ... Scale1={0} DrainValve1={1}",
|
2018-10-22 16:14:47 +00:00
|
|
|
|
tank.Name, (DrainValve1 != null) ? DrainValve1.Name : "---");
|
2017-03-28 10:38:47 +00:00
|
|
|
|
}
|
2021-04-12 10:00:05 +00:00
|
|
|
|
else if (tank == Tank2 && DrainValve2 == null)
|
2017-03-28 10:38:47 +00:00
|
|
|
|
{
|
2018-10-22 16:14:47 +00:00
|
|
|
|
DrainValve2 = tank.DrainValve;
|
2021-04-12 10:00:05 +00:00
|
|
|
|
log.WarnFormat("InitializeComponents() ... Scale2={0} DrainValve2={1}",
|
2018-10-22 16:14:47 +00:00
|
|
|
|
tank.Name, (DrainValve2 != null) ? DrainValve2.Name : "---");
|
2017-03-28 10:38:47 +00:00
|
|
|
|
}
|
2021-04-12 10:00:05 +00:00
|
|
|
|
else if (tank == Tank3 && DrainValve3 == null)
|
2017-03-28 10:38:47 +00:00
|
|
|
|
{
|
2018-10-22 16:14:47 +00:00
|
|
|
|
DrainValve3 = tank.DrainValve;
|
2021-04-12 10:00:05 +00:00
|
|
|
|
log.WarnFormat("InitializeComponents() ... Scale3={0} DrainValve3={1}",
|
2018-10-22 16:14:47 +00:00
|
|
|
|
tank.Name, (DrainValve3 != null) ? DrainValve3.Name : "---");
|
2017-03-28 10:38:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2014-07-28 07:54:35 +00:00
|
|
|
|
}
|
2015-08-24 14:04:31 +00:00
|
|
|
|
|
|
|
|
|
|
if (anyComponentIsInSimulMode)
|
|
|
|
|
|
return inSimulMode.ToString();
|
|
|
|
|
|
else
|
|
|
|
|
|
return null;
|
2014-07-28 07:54:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-01-06 12:05:27 +00:00
|
|
|
|
/// <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()
|
|
|
|
|
|
{
|
2021-04-07 19:53:22 +00:00
|
|
|
|
for (int i = devices.Count - 1; i >= 0; i--)
|
2018-02-12 21:26:18 +00:00
|
|
|
|
{
|
2021-04-07 19:53:22 +00:00
|
|
|
|
var dev = devices[i];
|
2018-02-12 21:26:18 +00:00
|
|
|
|
UiBridge.Bridge.OnActivity(null, string.Format("1. {0}", dev.Name)); /// Info
|
|
|
|
|
|
dev.StopDevice();
|
|
|
|
|
|
}
|
2017-01-06 12:05:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-05 18:26:07 +00:00
|
|
|
|
/// <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()
|
|
|
|
|
|
{
|
2021-04-07 19:53:22 +00:00
|
|
|
|
for (int i = devices.Count - 1; i >= 0; i--)
|
2018-02-12 21:26:18 +00:00
|
|
|
|
{
|
2021-04-07 19:53:22 +00:00
|
|
|
|
var dev = devices[i];
|
2018-02-12 21:26:18 +00:00
|
|
|
|
UiBridge.Bridge.OnActivity(null, string.Format("2. {0}", dev.Name)); /// Info
|
|
|
|
|
|
dev.StopDevice2();
|
|
|
|
|
|
}
|
2017-12-05 18:26:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-08-30 06:04:58 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Checks remote and local configuration DB paths and transitions for compatibility
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>true when DB-s are compatible</returns>
|
2023-04-18 12:01:23 +00:00
|
|
|
|
public static bool IsRemoteDBCompatible(ISession localSession, out string message)
|
2018-08-30 06:04:58 +00:00
|
|
|
|
{
|
2023-03-28 11:03:38 +00:00
|
|
|
|
IList<Config.Entities.FeedingPath> remoteFeedingPaths = new List<Config.Entities.FeedingPath>();
|
|
|
|
|
|
IList<Config.Entities.BenchPath> remoteBenchPaths = new List<Config.Entities.BenchPath>();
|
|
|
|
|
|
IList<Config.Entities.OutputPath> remoteOutputPaths = new List<Config.Entities.OutputPath>();
|
|
|
|
|
|
IList<Config.Entities.MetersPath> remoteMetersPaths = new List<Config.Entities.MetersPath>();
|
|
|
|
|
|
IList<Config.Entities.TransitionSequence> remoteTransitions = new List<Config.Entities.TransitionSequence>();
|
2018-10-13 04:44:00 +00:00
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2023-03-28 11:03:38 +00:00
|
|
|
|
var remoteSession = TBF.DB.SharedDBSessionFactory.OpenSession();
|
|
|
|
|
|
|
2018-10-13 04:44:00 +00:00
|
|
|
|
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();
|
2023-03-28 11:03:38 +00:00
|
|
|
|
|
|
|
|
|
|
remoteSession.Close();
|
2018-10-13 04:44:00 +00:00
|
|
|
|
}
|
2022-09-06 11:42:37 +00:00
|
|
|
|
catch (Exception exc)
|
2018-10-13 04:44:00 +00:00
|
|
|
|
{
|
2022-09-06 11:42:37 +00:00
|
|
|
|
message = (exc.InnerException != null)
|
|
|
|
|
|
? string.Format("\r\nException:\r\n{0}\r\nInner exception:\r\n{1}", exc.Message, exc.InnerException.Message)
|
|
|
|
|
|
: string.Format("\r\nException:\r\n{0}", exc.Message);
|
2018-10-13 04:44:00 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-08-30 06:04:58 +00:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-01-06 12:05:27 +00:00
|
|
|
|
|
2014-07-28 07:54:35 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Start the state machine
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static void Start()
|
|
|
|
|
|
{
|
2019-03-04 12:14:13 +00:00
|
|
|
|
if (MachineState != MachineState.StartingUp)
|
|
|
|
|
|
{
|
|
|
|
|
|
/// Unexpected MachineState (not MachineState.StartingUp) --> prevent worker thread from starting
|
|
|
|
|
|
///
|
|
|
|
|
|
MachineState = MachineState.FailedToStart;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2014-07-28 07:54:35 +00:00
|
|
|
|
|
|
|
|
|
|
workerThread = new Thread(Worker);
|
|
|
|
|
|
workerThread.CurrentCulture = Thread.CurrentThread.CurrentCulture;
|
|
|
|
|
|
workerThread.CurrentUICulture = Thread.CurrentThread.CurrentUICulture;
|
|
|
|
|
|
workerThread.Start();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-06-08 10:41:47 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Loads all paths and transitions from the DB.
|
|
|
|
|
|
/// Updates StatMachine.feedingPaths ... StatMachine.meterPaths, StatMachine.TransitionSequences
|
|
|
|
|
|
/// </summary>
|
2023-03-28 11:03:38 +00:00
|
|
|
|
public static void LoadPathsAndTransitions(ISession session = null)
|
2014-07-28 07:54:35 +00:00
|
|
|
|
{
|
2023-03-28 11:03:38 +00:00
|
|
|
|
bool openAndCloseSession = (session == null);
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (openAndCloseSession) session = TBF.DB.ConfigDBSessionFactory.OpenSession();
|
|
|
|
|
|
|
|
|
|
|
|
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();
|
2017-02-14 12:25:43 +00:00
|
|
|
|
#if HEAT_METERS
|
2023-03-28 11:03:38 +00:00
|
|
|
|
heatMetersPaths = session.QueryOver<Config.Entities.HeatMetersPath>().OrderBy(x => x.ItemNr).Asc.List();
|
2016-06-28 16:04:52 +00:00
|
|
|
|
#endif
|
2023-03-28 11:03:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
feedingPaths = new List<Config.Entities.FeedingPath>();
|
|
|
|
|
|
benchPaths = new List<Config.Entities.BenchPath>();
|
|
|
|
|
|
outputPaths = new List<Config.Entities.OutputPath>();
|
|
|
|
|
|
metersPaths = new List<Config.Entities.MetersPath>();
|
|
|
|
|
|
TransitionSequences = new List<TransitionSequence>();
|
|
|
|
|
|
TransitionSteps = new List<TransitionStep>();
|
|
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
if (openAndCloseSession && (session != null) && session.IsOpen) session.Close();
|
|
|
|
|
|
}
|
2018-06-08 10:41:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Loads selected procedure from the DB.
|
|
|
|
|
|
/// Updates StateMachine.Procedure and StateMachine.Tests
|
|
|
|
|
|
/// </summary>
|
2020-04-18 08:25:30 +00:00
|
|
|
|
public static bool LoadProcedure(ISession session, string procedureName, bool isRemote, IList<string> autoTests = null)
|
2018-06-08 10:41:47 +00:00
|
|
|
|
{
|
2018-06-15 07:38:18 +00:00
|
|
|
|
Procedure = null;
|
|
|
|
|
|
|
2023-03-28 11:03:38 +00:00
|
|
|
|
var selectedProcedure = session.QueryOver<Procedure>()
|
|
|
|
|
|
.Where(x => (x.ProcedureState == Common.ProcedureState.Active))
|
|
|
|
|
|
.And(x => (x.Name == procedureName))
|
|
|
|
|
|
.List();
|
|
|
|
|
|
|
|
|
|
|
|
if (selectedProcedure.Count == 1)
|
2020-04-18 08:25:30 +00:00
|
|
|
|
{
|
|
|
|
|
|
IsRemoteProcedure = isRemote;
|
2023-03-28 11:03:38 +00:00
|
|
|
|
Procedure = selectedProcedure[0];
|
|
|
|
|
|
TestInstances = selectedProcedure[0].UpdateTestInstances(LoopStartNames, LoopEndNames, autoTests);
|
2020-04-18 08:25:30 +00:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2021-01-21 14:00:55 +00:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2018-06-08 10:41:47 +00:00
|
|
|
|
}
|
2014-07-28 07:54:35 +00:00
|
|
|
|
|
2015-12-04 16:17:20 +00:00
|
|
|
|
public static void LoadProcedureParams(Procedure procedure)
|
2014-07-28 07:54:35 +00:00
|
|
|
|
{
|
|
|
|
|
|
foreach (var cmpnt in components)
|
|
|
|
|
|
{
|
2018-05-10 11:25:46 +00:00
|
|
|
|
cmpnt.Cfg.LoadProcedureParamsFromDB(procedure);
|
2014-07-28 07:54:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-06-19 07:25:24 +00:00
|
|
|
|
public static void LoadTestParams(Test test)
|
2014-07-28 07:54:35 +00:00
|
|
|
|
{
|
|
|
|
|
|
foreach (var cmpnt in components)
|
|
|
|
|
|
{
|
2018-05-10 11:25:46 +00:00
|
|
|
|
cmpnt.Cfg.LoadTestParamsFromDB(test);
|
2014-07-28 07:54:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <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>
|
2015-06-25 02:42:28 +00:00
|
|
|
|
/// <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>
|
2015-12-04 16:17:20 +00:00
|
|
|
|
public static bool GetPaths(Test test,
|
2016-07-07 07:52:54 +00:00
|
|
|
|
bool heatMetersPathRequired,
|
2015-05-20 14:18:54 +00:00
|
|
|
|
out FeedingPath pfeed,
|
|
|
|
|
|
out BenchPath pben,
|
|
|
|
|
|
out OutputPath pout,
|
|
|
|
|
|
out MetersPath pmtrs,
|
2016-06-28 16:04:52 +00:00
|
|
|
|
out HeatMetersPath phmtrs,
|
|
|
|
|
|
out TransitionSequence transitionBefore,
|
2018-11-14 07:23:01 +00:00
|
|
|
|
out TransitionSequence transitionBetween,
|
2015-12-04 16:17:20 +00:00
|
|
|
|
out TransitionSequence transitionAfter,
|
2015-05-20 14:18:54 +00:00
|
|
|
|
out string errorMsg)
|
2014-07-28 07:54:35 +00:00
|
|
|
|
{
|
2019-10-15 08:03:28 +00:00
|
|
|
|
ITestMethod tm = TbfComponents.FindComponent(test.Method) as ITestMethod;
|
|
|
|
|
|
bool isHydroTest = (tm != null) ? tm.DoTransitions() : false;
|
|
|
|
|
|
|
2014-07-28 07:54:35 +00:00
|
|
|
|
pfeed = null;
|
|
|
|
|
|
pben = null;
|
|
|
|
|
|
pout = null;
|
2016-06-28 16:04:52 +00:00
|
|
|
|
phmtrs = null;
|
2015-05-22 08:29:56 +00:00
|
|
|
|
transitionBefore = null;
|
2018-11-14 07:23:01 +00:00
|
|
|
|
transitionBetween = null;
|
|
|
|
|
|
transitionAfter = null;
|
2014-07-28 07:54:35 +00:00
|
|
|
|
|
|
|
|
|
|
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; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-09-04 11:49:04 +00:00
|
|
|
|
pmtrs = GetMetersPath(test);
|
2014-07-28 07:54:35 +00:00
|
|
|
|
|
2022-08-01 11:24:29 +00:00
|
|
|
|
if (isHydroTest && (pfeed == null)) errorMsg = string.Format(Strings.Test_0_config_error_1_is_missing, test.Name, Strings.Feeding_chdr);
|
|
|
|
|
|
else if (isHydroTest && (pben == null)) errorMsg = string.Format(Strings.Test_0_config_error_1_is_missing, test.Name, Strings.Bench_chdr);
|
|
|
|
|
|
else if (isHydroTest && (pout == null)) errorMsg = string.Format(Strings.Test_0_config_error_1_is_missing, test.Name, Strings.Output_chdr);
|
|
|
|
|
|
else if (pmtrs == null) errorMsg = string.Format(Strings.Test_0_config_error_1_is_missing, test.Name, Strings.Sensors_chdr);
|
2017-05-03 20:17:20 +00:00
|
|
|
|
else errorMsg = string.Empty;
|
2022-08-01 11:24:29 +00:00
|
|
|
|
|
2019-10-15 08:03:28 +00:00
|
|
|
|
if ((isHydroTest && (pfeed == null || pben == null || pout == null)) || (pmtrs == null))
|
2016-06-28 16:04:52 +00:00
|
|
|
|
{
|
2019-12-17 07:25:06 +00:00
|
|
|
|
log.ErrorFormat("GetPaths({0},...) returns false, wrong configuration", test.Name);
|
2016-06-28 16:04:52 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2014-07-28 07:54:35 +00:00
|
|
|
|
|
2017-02-14 12:25:43 +00:00
|
|
|
|
#if HEAT_METERS
|
2016-06-28 16:04:52 +00:00
|
|
|
|
foreach (var path in heatMetersPaths)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (test.HeatMetersPath == path.Name) { phmtrs = new HeatMetersPath(path, components); break; }
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
2016-07-07 07:52:54 +00:00
|
|
|
|
if (heatMetersPathRequired && phmtrs == null)
|
|
|
|
|
|
{
|
2019-12-17 07:25:06 +00:00
|
|
|
|
log.ErrorFormat("GetPaths({0},...) returns false, missing heat meters sensors", test.Name);
|
2016-07-07 07:52:54 +00:00
|
|
|
|
errorMsg = "Cannot load heat meters sensors";
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2016-06-28 16:04:52 +00:00
|
|
|
|
|
|
|
|
|
|
foreach (var tr in TransitionSequences)
|
|
|
|
|
|
{
|
2021-09-23 09:46:40 +00:00
|
|
|
|
if (tr.Name == test.TransBefore) transitionBefore = tr;
|
|
|
|
|
|
if (tr.Name == test.TransBetween) transitionBetween = tr;
|
|
|
|
|
|
if (tr.Name == test.TransAfter) transitionAfter = tr;
|
2016-06-28 16:04:52 +00:00
|
|
|
|
}
|
2015-05-20 14:18:54 +00:00
|
|
|
|
|
2019-10-15 08:03:28 +00:00
|
|
|
|
if (isHydroTest && pout.Scale == null)
|
2015-05-20 14:18:54 +00:00
|
|
|
|
{
|
2019-12-17 07:25:06 +00:00
|
|
|
|
log.ErrorFormat("GetPaths({0},...) returns false, missing scales in output path {1}", test.Name, test.OutputPath);
|
2015-05-20 14:18:54 +00:00
|
|
|
|
errorMsg = string.Format("No balance specified in path {0}", test.OutputPath);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-09-18 07:09:00 +00:00
|
|
|
|
if (Config.Formulas.RealDensity() < 500.0f || Config.Formulas.RealDensity() > 2000.0f)
|
2015-05-20 14:18:54 +00:00
|
|
|
|
{
|
2019-12-17 07:25:06 +00:00
|
|
|
|
log.ErrorFormat("GetPaths({0},...) returns false, density was not specified", test.Name);
|
|
|
|
|
|
errorMsg = string.Format("Density was not specified");
|
2015-05-20 14:18:54 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-12-17 07:25:06 +00:00
|
|
|
|
log.WarnFormat("GetPaths({0},...) returns true, paths loaded OK, isHydroTest={1}", test.Name, isHydroTest);
|
2015-05-20 14:18:54 +00:00
|
|
|
|
errorMsg = string.Empty;
|
|
|
|
|
|
return true;
|
2014-07-28 07:54:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-09-04 11:49:04 +00:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Updates paths based on the selected test
|
|
|
|
|
|
/// </summary>
|
2015-12-04 16:17:20 +00:00
|
|
|
|
public static MetersPath GetMetersPath(Test test)
|
2015-09-04 11:49:04 +00:00
|
|
|
|
{
|
|
|
|
|
|
MetersPath pmtrs = null;
|
|
|
|
|
|
foreach (var path in metersPaths)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (test.MetersPath == path.Name) { pmtrs = new MetersPath(path, components); break; }
|
|
|
|
|
|
}
|
|
|
|
|
|
if (pmtrs != null)
|
|
|
|
|
|
{
|
2021-09-19 20:04:16 +00:00
|
|
|
|
int count = Math.Min(TBF.Data.WMsCount, pmtrs.RegisterReaders.Length);
|
2015-09-04 11:49:04 +00:00
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ((pmtrs.RegisterReaders[i] != null) &&
|
2015-12-04 16:17:20 +00:00
|
|
|
|
(pmtrs.RegisterReaders[i].Cfg.DebugLevel == DebugMode.DetectedOff))
|
2015-09-04 11:49:04 +00:00
|
|
|
|
{
|
|
|
|
|
|
pmtrs.RegisterReaders[i] = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return pmtrs;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-07-28 07:54:35 +00:00
|
|
|
|
/*
|
|
|
|
|
|
* 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()
|
|
|
|
|
|
{
|
2019-03-04 12:14:13 +00:00
|
|
|
|
MachineState = MachineState.Running;
|
2014-07-28 07:54:35 +00:00
|
|
|
|
startDateTime = DateTime.Now;
|
2019-03-04 12:14:13 +00:00
|
|
|
|
CycleStartTimeStamp = startDateTime; /// Do not leave CycleStartTimeStamp uninitialized
|
2014-07-28 07:54:35 +00:00
|
|
|
|
|
|
|
|
|
|
/// Run all devices for the first time
|
|
|
|
|
|
foreach (var device in devices) device.RunDeviceBefore();
|
|
|
|
|
|
|
2019-03-04 12:14:13 +00:00
|
|
|
|
SequenceBase.ReferenceFlowmetersCount = SequenceBase.FlowMeters.Count;
|
2019-04-11 11:45:28 +00:00
|
|
|
|
SequenceBase.CalibratedLtrPerRefPulse = new double[SequenceBase.ReferenceFlowmetersCount];
|
2019-03-04 12:14:13 +00:00
|
|
|
|
foreach (var flowmtr in SequenceBase.FlowMeters)
|
|
|
|
|
|
{
|
|
|
|
|
|
int ix = flowmtr.Idx1;
|
|
|
|
|
|
if (ix > 0 && ix <= SequenceBase.ReferenceFlowmetersCount)
|
2014-09-06 15:34:26 +00:00
|
|
|
|
{
|
2019-04-11 11:45:28 +00:00
|
|
|
|
SequenceBase.CalibratedLtrPerRefPulse[ix - 1] = flowmtr.NominalFlow / 7200.0;
|
2014-09-06 15:34:26 +00:00
|
|
|
|
}
|
2018-02-16 05:54:14 +00:00
|
|
|
|
}
|
2019-03-04 12:14:13 +00:00
|
|
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 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
|
2014-07-28 07:54:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Do stuff that is repeated in the state execution loops most often
|
|
|
|
|
|
/// </summary>
|
2017-04-03 13:15:31 +00:00
|
|
|
|
/// <returns>List of Event-s returned from the state operations, null == quit</returns>
|
2019-03-04 12:14:13 +00:00
|
|
|
|
public static IList<Event> WaitRunDevsRunOps(bool lastTime = false)
|
2014-07-28 07:54:35 +00:00
|
|
|
|
{
|
2018-12-12 10:36:05 +00:00
|
|
|
|
Thread.Sleep(100); /// Insert 1/10 sec.delay between RunOperations() and RunDeviceAfter()
|
|
|
|
|
|
|
2021-04-07 19:53:22 +00:00
|
|
|
|
for (int i = devices.Count - 1; i >= 0; i-- )
|
2019-03-04 12:14:13 +00:00
|
|
|
|
{
|
2021-04-07 19:53:22 +00:00
|
|
|
|
devices[i].RunDeviceAfter();
|
2019-03-04 12:14:13 +00:00
|
|
|
|
}
|
2018-02-16 05:54:14 +00:00
|
|
|
|
|
2019-03-04 12:14:13 +00:00
|
|
|
|
WaitNextTick();
|
|
|
|
|
|
|
|
|
|
|
|
if (lastTime)
|
2018-02-16 05:54:14 +00:00
|
|
|
|
{
|
|
|
|
|
|
///
|
2019-03-04 12:14:13 +00:00
|
|
|
|
/// Executed when the state machine is being stopped
|
2018-02-16 05:54:14 +00:00
|
|
|
|
///
|
|
|
|
|
|
State.StopOperations();
|
2019-03-04 12:14:13 +00:00
|
|
|
|
return null;
|
2018-02-16 05:54:14 +00:00
|
|
|
|
}
|
2018-12-12 10:36:05 +00:00
|
|
|
|
|
2019-03-04 12:14:13 +00:00
|
|
|
|
foreach (var device in devices)
|
|
|
|
|
|
{
|
|
|
|
|
|
device.RunDeviceBefore();
|
|
|
|
|
|
}
|
2018-12-12 10:36:05 +00:00
|
|
|
|
|
2019-03-04 12:14:13 +00:00
|
|
|
|
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
|
2014-07-28 07:54:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Wait time period - synchronize
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>true when interrupted by 'quitStateMachine', otherwise false</returns>
|
2019-03-04 12:14:13 +00:00
|
|
|
|
public static void WaitNextTick()
|
2014-07-28 07:54:35 +00:00
|
|
|
|
{
|
2021-10-08 09:36:38 +00:00
|
|
|
|
int currentTimeSecFromClock = Convert.ToInt32(Math.Round((DateTime.Now - startDateTime).TotalSeconds - 0.2));
|
|
|
|
|
|
|
|
|
|
|
|
currentTimeSec = Math.Max(currentTimeSec, currentTimeSecFromClock) + Period;
|
|
|
|
|
|
|
2019-03-04 12:14:13 +00:00
|
|
|
|
DateTime nextTickDateTime = startDateTime + TimeSpan.FromSeconds(currentTimeSec);
|
2014-07-28 07:54:35 +00:00
|
|
|
|
|
2019-03-04 12:14:13 +00:00
|
|
|
|
while (DateTime.Now < nextTickDateTime)
|
2014-07-28 07:54:35 +00:00
|
|
|
|
{
|
2017-12-04 16:51:59 +00:00
|
|
|
|
Thread.Sleep(100);
|
2014-07-28 07:54:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|