diff --git a/TBF/BenchControl/Output/FlexFlow/Rest/Factory.cs b/TBF/BenchControl/Output/FlexFlow/Factory.cs
similarity index 93%
rename from TBF/BenchControl/Output/FlexFlow/Rest/Factory.cs
rename to TBF/BenchControl/Output/FlexFlow/Factory.cs
index d714e85b5..8712e4ab4 100644
--- a/TBF/BenchControl/Output/FlexFlow/Rest/Factory.cs
+++ b/TBF/BenchControl/Output/FlexFlow/Factory.cs
@@ -4,7 +4,7 @@
using System.Collections.Generic;
using TBF.BenchControl.Generic;
-namespace TBF.BenchControl.Output.FlexFlow.Rest
+namespace TBF.BenchControl.Output.FlexFlow
{
public class Factory : IComponentFactory
{
diff --git a/TBF/BenchControl/Output/FlexFlow/FlexFlow.cs b/TBF/BenchControl/Output/FlexFlow/FlexFlow.cs
new file mode 100644
index 000000000..51fb5052d
--- /dev/null
+++ b/TBF/BenchControl/Output/FlexFlow/FlexFlow.cs
@@ -0,0 +1,566 @@
+///
+/// Copyright (c) 2021 Sensus Slovensko a.s.
+///
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Net.Http;
+using log4net;
+using Config.Entities;
+using System.Text;
+
+namespace TBF.BenchControl.Output.FlexFlow
+{
+ public class FlexFlow : ComponentBase, IOperation, GenericDevices.IResultsWriter, GenericDevices.IStartInfoReader, Generic.IDevice
+ {
+ private static readonly ILog log = LogManager.GetLogger(typeof(FlexFlow));
+ public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); }
+
+ FlexFlowCfg flexFlowCfg;
+
+ public string StationName
+ {
+ get
+ {
+ var benchInfo = TBF.BenchControl.Sequences.ProcessData.BenchInfo;
+ return (benchInfo != null) ? benchInfo.TestBenchName : "TestBench";
+ }
+ }
+
+ public string UserId { get { return Users.GlobalData.GetCurrentUserName(); } }
+
+
+ enum Retv
+ {
+ OK,
+ Error,
+ }
+
+ enum OpState
+ {
+ None,
+ GetUnitInfosScheduled,
+ GetUnitInfosRunning,
+ SaveResultsScheduled,
+ SaveResultsRunning,
+ }
+ ///
+ OpState currentOpState;
+ bool opCompleted;
+ bool anyError;
+
+
+ ///
+ /// Watermeters to check at the beginning of the cycle
+ ///
+ IList waterMeters;
+
+ ///
+ /// Data (tracing records) to write at the end of cycle
+ ///
+ Results.Entities.Batch batch;
+ DateTime resultsTimeStamp;
+ string resultsTimeStampStr;
+
+
+ public FlexFlow() {}
+
+ public FlexFlow(Generic.IComponentCfg cfg)
+ : base(cfg)
+ {
+ flexFlowCfg = cfg as FlexFlowCfg;
+ }
+
+ public override void Initialize()
+ {
+ currentOpState = OpState.None;
+ resultsTimeStampStr = string.Empty;
+
+ if (flexFlowCfg.DebugLevel == DebugMode.Normal)
+ {
+ /// TODO: Initialize FlexFlow
+
+ log.FatalFormat("{0} initialized: {1}", Name, this);
+ }
+ else
+ {
+ log.FatalFormat("{0} simulated: {1}", Name, this);
+ }
+ }
+
+ ///
+ /// IDevice interface implementation
+ ///
+ public void RunDeviceBefore() { }
+ public void RunDeviceAfter() { }
+ ///
+ public void StopDevice()
+ {
+ if (flexFlowCfg.DebugLevel != DebugMode.Normal) return;
+
+ /// TODO: Close FlexFlow
+ }
+ public void StopDevice2() {}
+
+
+ ///
+ /// Reads information on start of a cycle, Events: Event.InfoRead
+ ///
+ /// Results of water meters
+ /// Reference to the operation
+ public IOperation ReadStartInfoOp(IList waterMeters)
+ {
+ if (!flexFlowCfg.CheckPreviousRecords)
+ {
+ return null;
+ }
+ else if ((currentOpState == OpState.GetUnitInfosRunning) || (currentOpState == OpState.SaveResultsRunning))
+ {
+ throw new Exception("Sequence error");
+ }
+ else
+ {
+ this.waterMeters = waterMeters;
+ currentOpState = OpState.GetUnitInfosScheduled;
+ return this;
+ }
+ }
+
+ ///
+ /// Writes the test cycle results into a file, Events: Event.ResultsWritten
+ ///
+ /// Procedure to print the results of
+ /// Results to write into the file
+ /// Reference to the operation
+ public IOperation ProcessResultsOp(Results.Entities.Batch batch)
+ {
+ if (!flexFlowCfg.SaveResults)
+ {
+ log.WarnFormat("{0} saving results disabled", Name);
+ return null;
+ }
+ else if ((currentOpState == OpState.GetUnitInfosRunning) || (currentOpState == OpState.SaveResultsRunning))
+ {
+ log.WarnFormat("{0} sequence error", Name);
+ throw new Exception("Sequence error");
+ }
+ else
+ {
+ this.batch = batch;
+ this.resultsTimeStamp = batch.EndTime;
+ this.resultsTimeStampStr = batch.EndTime.ToString("yyyy-MM-dd HH:mm:ss");
+ currentOpState = OpState.SaveResultsScheduled;
+ return this;
+ }
+ }
+
+
+ /// Start this operation
+ public void Start()
+ {
+ var oriOpState = currentOpState;
+
+ if (currentOpState == OpState.GetUnitInfosScheduled)
+ {
+ currentOpState = OpState.GetUnitInfosRunning;
+ }
+ else if (currentOpState == OpState.SaveResultsScheduled)
+ {
+ currentOpState = OpState.SaveResultsRunning;
+ }
+
+ opCompleted = false;
+ anyError = false;
+ log.WarnFormat("{0} Start() completed, op.state {1} --> {2}", Name, oriOpState, currentOpState);
+ }
+
+ /// Run this operation
+ /// Event.ResultsWritten or Event.Error
+ public Event Run()
+ {
+ var oriOpState = currentOpState;
+
+ if (currentOpState == OpState.GetUnitInfosRunning)
+ {
+ if (flexFlowCfg.DebugLevel == DebugMode.Simulate)
+ {
+ return Event.InfoRead;
+ }
+ else if (!opCompleted)
+ {
+ log.WarnFormat("{0} : Run() : currentOp = {1}", Name, currentOpState);
+ opCompleted = true;
+ if (GetUnitInfos(waterMeters) != Retv.OK)
+ {
+ anyError = true;
+ }
+
+ var retval = anyError ? Event.InfoNotRead : Event.InfoRead;
+ log.WarnFormat("{0} Run() returned {1}, op.state {2} --> {3}", Name, retval, oriOpState, currentOpState);
+ return retval;
+ }
+ else
+ {
+ var retval = anyError ? Event.InfoNotRead : Event.InfoRead;
+ log.WarnFormat("{0} Run() returned {1}, op.state {2} --> {3}", Name, retval, oriOpState, currentOpState);
+ return retval;
+ }
+ }
+ else if (currentOpState == OpState.SaveResultsRunning)
+ {
+ if (flexFlowCfg.DebugLevel == DebugMode.Simulate || batch.WaterMeters.Count == 0)
+ {
+ return Event.ResultsWritten;
+ }
+ else if (!opCompleted)
+ {
+ opCompleted = true;
+ if (SaveResults(batch) != Retv.OK)
+ {
+ anyError = true;
+ }
+ var retval = anyError ? Event.ErrorProcessingResults : Event.ResultsWritten;
+ log.WarnFormat("{0} Run() returned {1}, op.state {2} --> {3}", Name, retval, oriOpState, currentOpState);
+ return retval;
+ }
+ else
+ {
+ var retval = anyError ? Event.ErrorProcessingResults : Event.ResultsWritten;
+ log.WarnFormat("{0} Run() returned {1}, op.state {2} --> {3}", Name, retval, oriOpState, currentOpState);
+ return retval;
+ }
+ }
+ else
+ {
+ log.WarnFormat("{0} Run() returned {1}, op.state {2} --> {3}", Name, Event.None, oriOpState, currentOpState);
+ return Event.None;
+ }
+ }
+
+ /// Stop this operation
+ public void Stop()
+ {
+ var oriOpState = currentOpState;
+ currentOpState = OpState.None;
+ log.WarnFormat("{0} Stop() completed, op.state {1} --> {2}", Name, oriOpState, currentOpState);
+ }
+
+
+ Retv GetUnitInfos(IList waterMeters)
+ {
+ bool allOK = true;
+
+ foreach (var wm in waterMeters)
+ {
+ if (wm != null && !wm.Disabled)
+ {
+ GetOneUnitInfo(wm, (flexFlowCfg.GetUnitInfoApi == WebApi.REST));
+
+ if (wm.LastRecordIsNok)
+ {
+ allOK = false;
+ }
+ }
+ }
+
+ return allOK ? Retv.OK : Retv.Error;
+ }
+
+
+ Retv GetOneUnitInfo(Results.Entities.WaterMeter wm, bool isRest)
+ {
+ string serviceName = isRest ? "GetUnitInfo REST" : "GetUnitInfo SOAP"; /// For logging only
+ string url = flexFlowCfg.BaseUrl + flexFlowCfg.GetUnitInfoUrl; /// Complete URL from base URL and relative URL
+ string localUserId = (!flexFlowCfg.SendUserId || string.IsNullOrEmpty(UserId)) ? "admin" : UserId;
+ log.DebugFormat("{0} URL is {1}", serviceName, url);
+
+ if (string.IsNullOrEmpty(wm.SerialNr))
+ {
+ /// S/N is missing
+ wm.LastRecordIsNok = false; /// OK, this is an RFID comm. error, not a production tracing error
+ log.DebugFormat("{0} : pcbNr is missing", serviceName);
+ return Retv.OK;
+ }
+
+ string request;
+ if (isRest)
+ {
+ /// JSON request formatting
+ request = string.Format("{{{0}\"StationName\": \"{1}\",{0}\"UserID\": \"{2}\",{0}\"SerialNumber\": \"{3}\",{0}}}",
+ string.Empty, //Environment.NewLine,
+ StationName,
+ localUserId,
+ wm.SerialNr);
+ }
+ else
+ {
+ /// TODO: Implement XML request formatting
+ request = string.Format("{{{0}\"StationName\": \"{1}\",{0}\"UserID\": \"{2}\",{0}\"SerialNumber\": \"{3}\",{0}}}",
+ string.Empty, //Environment.NewLine,
+ StationName,
+ localUserId,
+ wm.SerialNr);
+ }
+ log.DebugFormat("{0} request (pcbNr = {1}):\r\n{2}", serviceName, wm.SerialNr, request);
+
+ /// Object to be updated when parsing the response
+ UnitInfoData data = new UnitInfoData(wm.SerialNr, StationName, localUserId);
+
+ try
+ {
+ var webRequest = isRest ? CreateRestHttpWebRequest(url) : CreateSoapHttpWebRequest(url);
+
+ using (var streamWriter = new StreamWriter(webRequest.GetRequestStream()))
+ {
+ streamWriter.Write(request);
+ }
+
+ using (var streamReader = new StreamReader(webRequest.GetResponse().GetResponseStream()))
+ {
+ var response = streamReader.ReadToEnd();
+
+ log.DebugFormat("{0} response (pcbNr={1}):\r\n{2}", serviceName, wm.SerialNr, response);
+
+ if (isRest)
+ {
+ /// JSON response parsing
+ string pattern = "\"id\":";
+ int position = response.IndexOf(pattern) + pattern.Length;
+ int length = response.Substring(position).IndexOfAny(new char[] { ',', '}', '\r', '\n' });
+ int idummy;
+ if (int.TryParse(response.Substring(position, length).Trim(), out idummy))
+ {
+ data.Id = idummy;
+ }
+
+ pattern = "\"value\":";
+ position = response.IndexOf(pattern) + pattern.Length;
+ length = response.Substring(position).IndexOfAny(new char[] { ',', '}', '\r', '\n' });
+ data.Value = response.Substring(position, length);
+
+ pattern = "\"ffUnitId\":";
+ position = response.IndexOf(pattern) + pattern.Length;
+ length = response.Substring(position).IndexOfAny(new char[] { ',', '}', '\r', '\n' });
+ long ldummy;
+ if (long.TryParse(response.Substring(position, length).Trim(), out ldummy))
+ {
+ data.FFUnitId = ldummy;
+ }
+
+ pattern = "\"ffTestId\":";
+ position = response.IndexOf(pattern) + pattern.Length;
+ length = response.Substring(position).IndexOfAny(new char[] { ',', '}', '\r', '\n' });
+ if (long.TryParse(response.Substring(position, length).Trim(), out ldummy))
+ {
+ data.FFTestId = ldummy;
+ }
+
+ pattern = "\"ffPartId\":";
+ position = response.IndexOf(pattern) + pattern.Length;
+ length = response.Substring(position).IndexOfAny(new char[] { ',', '}', '\r', '\n' });
+ if (long.TryParse(response.Substring(position, length).Trim(), out ldummy))
+ {
+ data.FFPartId = ldummy;
+ }
+ }
+ else
+ {
+ ///
+ }
+ }
+ }
+ catch (Exception exc)
+ {
+ log.ErrorFormat("{0} failed: {1}", serviceName, exc.Message);
+ }
+
+ return (data != null && data.Id == 0) ? Retv.OK : Retv.Error;
+ }
+
+
+ ///
+ /// Write results of a batch of water meters to the DB
+ ///
+ /// Batch entity
+ Retv SaveResults(Results.Entities.Batch batch)
+ {
+ try
+ {
+ int writtenRecordsCount = 0;
+ foreach (var wMtr in batch.WaterMeters)
+ {
+ writtenRecordsCount += SaveOneUnitResult(wMtr, (flexFlowCfg.SaveResultsApi == WebApi.REST));
+ }
+
+ log.ErrorFormat("Batch {0}: {1} of {2} watermeters written to the regular production tracing DB",
+ batch.BatchNr, writtenRecordsCount, batch.WaterMeters.Count);
+ }
+ catch (Exception exc)
+ {
+ log.WarnFormat("Failed to write results of batch {0} to production tracing DB: {1}",
+ batch.BatchNr, exc.Message);
+ return Retv.Error;
+ }
+
+ return Retv.OK;
+ }
+
+
+ ///
+ /// Write one meter results to the DB
+ ///
+ /// Watermeter entity
+ /// Number of written water meters (0 or 1)
+ int SaveOneUnitResult(Results.Entities.WaterMeter wm, bool isRest)
+ {
+ string serviceName = isRest ? "SaveTestResult REST" : "SaveTestResult SOAP"; /// For logging only
+ string url = flexFlowCfg.BaseUrl + flexFlowCfg.SaveResultsUrl; /// Complete URL from base URL and relative URL
+ string localUserId = (!flexFlowCfg.SendUserId || string.IsNullOrEmpty(UserId)) ? "admin" : UserId;
+ log.DebugFormat("{0} URL is {1}", serviceName, url);
+
+ if (string.IsNullOrEmpty(wm.SerialNr))
+ {
+ /// S/N is missing
+ log.DebugFormat("{0} : pcbNr is missing", serviceName);
+ return 0;
+ }
+
+ ///
+ /// Request formatting
+ ///
+ string request;
+ if (isRest)
+ {
+ /// JSON request formatting
+ string status = wm.Passed ? "Passed" : "Failed";
+ StringBuilder sb = new StringBuilder();
+ sb.AppendLine(string.Format("{{"));
+ sb.AppendLine(string.Format(" \"timestamp\": \"{0:yyyy-MM-dd HH:mm:ss}\",", DateTime.Now));
+ sb.AppendLine(string.Format(" \"syntaxRev\": \"1.1\","));
+ sb.AppendLine(string.Format(" \"compatibleRev\": \"1.1\","));
+ sb.AppendLine(string.Format(" \"factory\": {{"));
+ sb.AppendLine(string.Format(" \"name\": \"{0}\",", "Flex Chennai"));
+ sb.AppendLine(string.Format(" \"line\": \"\","));
+ sb.AppendLine(string.Format(" \"tester\": \"{0}\",", StationName));
+ sb.AppendLine(string.Format(" \"user\": \"{0}\"", localUserId));
+ sb.AppendLine(string.Format(" }},"));
+ sb.AppendLine(string.Format(" \"product\": {{"));
+ sb.AppendLine(string.Format(" \"name\": \"{0}\",", "iPERL"));
+ sb.AppendLine(string.Format(" \"revision\": \"{0}\"", "S4"));
+ sb.AppendLine(string.Format(" }},"));
+ sb.AppendLine(string.Format(" \"panel\": {{"));
+ sb.AppendLine(string.Format(" \"id\": \"\","));
+ sb.AppendLine(string.Format(" \"comment\": \"\","));
+ sb.AppendLine(string.Format(" \"runmode\": \"{0}\",", "Production"));
+ sb.AppendLine(string.Format(" \"timestamp\": \"{0:yyyy-MM-dd HH:mm:ss}\",", DateTime.Now));
+ sb.AppendLine(string.Format(" \"testTime\": \"{0}\",", 0));
+ sb.AppendLine(string.Format(" \"status\": \"{0}\",", status));
+ sb.AppendLine(string.Format(" \"dut\": [{{"));
+ sb.AppendLine(string.Format(" \"id\": \"{0}\",", wm.SerialNr));
+ sb.AppendLine(string.Format(" \"comment\": \"\","));
+ sb.AppendLine(string.Format(" \"panel\": \"{0}\",", 1));
+ sb.AppendLine(string.Format(" \"socket\": \"{0}\",", 0));
+ sb.AppendLine(string.Format(" \"timestamp\": \"{0:yyyy-MM-dd HH:mm:ss}\",", resultsTimeStamp));
+ sb.AppendLine(string.Format(" \"runmode\": \"{0}\",", "Production"));
+ sb.AppendLine(string.Format(" \"testTime\": \"{0}\",", (wm.Batch.EndTime - wm.Batch.StartTime).TotalSeconds.ToString("F0")));
+ sb.AppendLine(string.Format(" \"status\": \"{0}\",", status));
+ sb.AppendLine(string.Format(" \"group\": {{"));
+ sb.AppendLine(string.Format(" \"name\": \"{0}\",", "iPERL"));
+ sb.AppendLine(string.Format(" \"groupIndex\": \"{0}\",", 0));
+ sb.AppendLine(string.Format(" \"loopIndex\": \"{0}\",", 0));
+ sb.AppendLine(string.Format(" \"type\": \"{0}\",", "SequenceCall"));
+ sb.AppendLine(string.Format(" \"moduleTime\": \"{0}\",", 0));
+ sb.AppendLine(string.Format(" \"totalTime\": \"{0}\",", (wm.Batch.EndTime - wm.Batch.StartTime).TotalSeconds.ToString("F0")));
+ sb.AppendLine(string.Format(" \"timestamp\": \"{0:yyyy-MM-dd HH:mm:ss}\",", resultsTimeStamp));
+ sb.AppendLine(string.Format(" \"status\": \"{0}\"", status));
+ sb.AppendLine(string.Format(" }}"));
+ sb.AppendLine(string.Format(" }}]"));
+ sb.AppendLine(string.Format(" }}"));
+ sb.AppendLine(string.Format("}}"));
+ request = sb.ToString();
+ }
+ else
+ {
+ /// XML request formatting
+ string status = wm.Passed ? "PASS" : "FAIL";
+ StringBuilder sb = new StringBuilder();
+ sb.AppendLine(string.Format("", resultsTimeStampStr));
+ sb.AppendLine(string.Format(" {0}", resultsTimeStampStr));
+ sb.AppendLine(string.Format(" "));
+ sb.AppendLine(string.Format(" "));
+ sb.AppendLine(string.Format(" ", wm.SerialNr, resultsTimeStampStr, status));
+ sb.AppendLine(string.Format(" ", wm.SerialNr, resultsTimeStampStr, status));
+ sb.AppendLine(string.Format(" ", wm.SerialNr, resultsTimeStampStr, status));
+ sb.AppendLine(string.Format(" ", status));
+ sb.AppendLine(string.Format(" "));
+ sb.AppendLine(string.Format(" "));
+ sb.AppendLine(string.Format(" "));
+ sb.AppendLine(string.Format(""));
+ request = sb.ToString();
+ }
+ log.DebugFormat("{0} request (pcbNr = {1}):\r\n{2}", serviceName, wm.SerialNr, request);
+
+ /// Object to be updated when parsing the response
+ ResultsData data = new ResultsData(wm.SerialNr, StationName, localUserId);
+
+ try
+ {
+ var webRequest = isRest ? CreateRestHttpWebRequest(url) : CreateSoapHttpWebRequest(url);
+
+ using (var streamWriter = new StreamWriter(webRequest.GetRequestStream()))
+ {
+ streamWriter.Write(request);
+ }
+
+ using (var streamReader = new StreamReader(webRequest.GetResponse().GetResponseStream()))
+ {
+ var response = streamReader.ReadToEnd();
+
+ log.DebugFormat("{0} response (pcbNr={1}):\r\n{2}", serviceName, wm.SerialNr, response);
+
+ if (isRest)
+ {
+ /// JSON response parsing
+ string pattern = "\"id\":";
+ int position = response.IndexOf(pattern) + pattern.Length;
+ int length = response.Substring(position).IndexOfAny(new char[] { ',', '}', '\r', '\n' });
+ int idummy;
+ if (int.TryParse(response.Substring(position, length).Trim(), out idummy))
+ {
+ data.Id = idummy;
+ }
+ }
+ else
+ {
+ /// TODO: XML resonse parsing
+ }
+ }
+ }
+ catch (Exception exc)
+ {
+ log.ErrorFormat("{0} failed: {1}", serviceName, exc.Message);
+ }
+
+ return (data != null && data.Id == 0) ? 1 : 0;
+ }
+
+ public HttpWebRequest CreateRestHttpWebRequest(string url)
+ {
+ var request = (HttpWebRequest)WebRequest.Create(url);
+ request.ContentType = "application/json";
+ request.Method = "POST";
+ return request;
+ }
+
+ public HttpWebRequest CreateSoapHttpWebRequest(string url)
+ {
+ var request = (HttpWebRequest)WebRequest.Create(url);
+ request.Headers.Add(@"SOAPAction:http://tempuri.org/Addition");
+ request.ContentType = "application/xml";
+ request.Accept = "text/xml";
+ request.Method = "POST";
+ return request;
+ }
+ }
+}
diff --git a/TBF/BenchControl/Output/FlexFlow/FlexFlowCfg.cs b/TBF/BenchControl/Output/FlexFlow/FlexFlowCfg.cs
new file mode 100644
index 000000000..81fefe0e2
--- /dev/null
+++ b/TBF/BenchControl/Output/FlexFlow/FlexFlowCfg.cs
@@ -0,0 +1,198 @@
+///
+/// Copyright (c) 2021 Sensus Slovensko a.s.
+///
+using System;
+using System.Collections.Generic;
+using System.Xml.Serialization;
+using Config.Entities;
+using TBF.BenchControl.Generic;
+using TBF.Resources;
+
+namespace TBF.BenchControl.Output.FlexFlow
+{
+ public enum WebApi
+ {
+ REST,
+ SOAP,
+ }
+
+ ///
+ /// Holds FlexFlow.Rest configuration.
+ ///
+ public class FlexFlowCfg : ComponentCfgBase, Generic.IComponentCfg, IParamsProvider
+ {
+ public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(FlexFlowCfg) })[0];
+ public override XmlSerializer GetSerializer() { return Serializer; }
+
+ public IComponentCfgCtrl GetControl(IList cmpntEntities) { return new Configs.ParamsProvider.ComponentCfgCtrl(this, null); }
+
+ ///
+ /// Serialized parameters
+ ///
+ public string BaseUrl;
+ public WebApi GetUnitInfoApi;
+ public string GetUnitInfoUrl;
+ public WebApi SaveResultsApi;
+ public string SaveResultsUrl;
+ public bool CheckPreviousRecords;
+ public bool SaveResults;
+ public bool SendUserId;
+
+
+ /// Private parameterless constructor invoked by all other (public) constructors
+ FlexFlowCfg() { }
+
+ public FlexFlowCfg(string name, IComponentFactory factory)
+ : this()
+ {
+ Name = name;
+ Factory = factory;
+ ParentName = string.Empty;
+ InitializeAll();
+ }
+
+ public string ComponentName { get { return Name; } }
+
+ public void InitializeAll()
+ {
+ BaseUrl = "http://gdlm02205:4450/?wsdl";
+ GetUnitInfoUrl = string.Empty;
+ SaveResultsUrl = string.Empty;
+ CheckPreviousRecords = true;
+ SaveResults = true;
+ SendUserId = true;
+ }
+
+ string[] paramNames = new string[]
+ {
+ "Base URL", /// 0
+ "Get unit info", /// 1
+ "GetUnitInfo Web API", /// 2
+ "GetUnitInfo URL relative to base URL", /// 3
+ "Save result", /// 4
+ "SaveResult Web API", /// 5
+ "SaveResult URL relative to base URL", /// 6
+ "Send user ID", /// 7
+ };
+ public string ParamName(int i) { return paramNames[i]; }
+ public int ParamsCount() { return paramNames.Length; }
+
+ public ICollection ParamValues(int i)
+ {
+ switch (i)
+ {
+ case 1:
+ case 4:
+ case 7:
+ return new string[] { Strings.yes, Strings.no };
+ case 2:
+ case 5:
+ return new string[] { WebApi.REST.ToString(), WebApi.SOAP.ToString() };
+ default:
+ return null;
+ }
+ }
+
+ public string ToString(int i)
+ {
+ switch (i)
+ {
+ case 0: return BaseUrl;
+ case 1: return CheckPreviousRecords ? Strings.yes : Strings.no;
+ case 2: return GetUnitInfoApi.ToString();
+ case 3: return GetUnitInfoUrl;
+ case 4: return SaveResults ? Strings.yes : Strings.no;
+ case 5: return SaveResultsApi.ToString();
+ case 6: return SaveResultsUrl;
+ case 7: return SendUserId ? Strings.yes : Strings.no;
+ default:
+ return string.Format("{0} CheckPrev.={1}, SaveResults={2}, URL={3}, URL2={4}",
+ Name,
+ CheckPreviousRecords,
+ SaveResults,
+ BaseUrl,
+ GetUnitInfoUrl);
+ }
+ }
+
+ public CfgUpdateFlags UpdateParam(int i, string str)
+ {
+ switch (i)
+ {
+ case 0: BaseUrl = str; return CfgUpdateFlags.RestartRqrd;
+ case 1: CheckPreviousRecords = (str == Strings.yes); return CfgUpdateFlags.RestartRqrd;
+ case 2:
+ foreach (var wa in new WebApi[] { WebApi.REST, WebApi.SOAP })
+ if (str == wa.ToString())
+ {
+ GetUnitInfoApi = wa;
+ return CfgUpdateFlags.RestartRqrd;
+ }
+ return CfgUpdateFlags.None;
+ case 3: GetUnitInfoUrl = str; return CfgUpdateFlags.RestartRqrd;
+ case 4: SaveResults = (str == Strings.yes); return CfgUpdateFlags.RestartRqrd;
+ case 5:
+ foreach (var wa in new WebApi[] { WebApi.REST, WebApi.SOAP })
+ if (str == wa.ToString())
+ {
+ SaveResultsApi = wa;
+ return CfgUpdateFlags.RestartRqrd;
+ }
+ return CfgUpdateFlags.None;
+ case 6: SaveResultsUrl = str; return CfgUpdateFlags.RestartRqrd;
+ case 7: SendUserId = (str == Strings.yes); return CfgUpdateFlags.RestartRqrd;
+ default: return CfgUpdateFlags.None;
+ }
+ }
+
+ public bool ValidateParam(int i, string strValue, out string message)
+ {
+ message = string.Empty;
+
+ switch (i)
+ {
+ case 0:
+ case 3:
+ case 6:
+ return true;
+ case 1:
+ case 2:
+ case 4:
+ case 5:
+ case 7:
+ if (ParamValues(i).Contains(strValue)) return true;
+ break;
+ default:
+ message = "Invalid index";
+ return false;
+ }
+
+ message = ParamName(i) + " is invalid";
+ return false;
+ }
+
+ void CopyContentTo(FlexFlowCfg prms)
+ {
+ prms.BaseUrl = this.BaseUrl;
+ prms.CheckPreviousRecords = this.CheckPreviousRecords;
+ prms.GetUnitInfoApi = this.GetUnitInfoApi;
+ prms.GetUnitInfoUrl = this.GetUnitInfoUrl;
+ prms.SaveResults = this.SaveResults;
+ prms.SaveResultsApi = this.SaveResultsApi;
+ prms.SaveResultsUrl = this.SaveResultsUrl;
+ prms.SendUserId = this.SendUserId;
+ }
+
+ public Config.Entities.IParamsProvider Clone()
+ {
+ FlexFlowCfg pars = new FlexFlowCfg();
+ CopyContentTo(pars);
+ return pars;
+ }
+
+ public bool UpdateEmbeddedDbEntity()
+ {
+ return true; /// =OK, do nothing
+ }
+ }
+}
diff --git a/TBF/BenchControl/Output/FlexFlow/Rest/FlexFlow.cs b/TBF/BenchControl/Output/FlexFlow/Rest/FlexFlow.cs
deleted file mode 100644
index b49c58aee..000000000
--- a/TBF/BenchControl/Output/FlexFlow/Rest/FlexFlow.cs
+++ /dev/null
@@ -1,323 +0,0 @@
-///
-/// Copyright (c) 2021 Sensus Slovensko a.s.
-///
-using System;
-using System.Collections.Generic;
-using log4net;
-using Config.Entities;
-
-namespace TBF.BenchControl.Output.FlexFlow.Rest
-{
- public class FlexFlow : ComponentBase, IOperation, GenericDevices.IResultsWriter, GenericDevices.IStartInfoReader, Generic.IDevice
- {
- private static readonly ILog log = LogManager.GetLogger(typeof(FlexFlow));
- public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); }
-
- FlexFlowCfg flexFlowCfg;
-
- public string StationName
- {
- get
- {
- var benchInfo = TBF.BenchControl.Sequences.ProcessData.BenchInfo;
- return (benchInfo != null) ? benchInfo.TestBenchName : "TestBench";
- }
- }
-
- public string UserID { get { return Users.GlobalData.GetCurrentUserName(); } }
-
-
- enum Retv
- {
- OK,
- Error,
- }
-
- enum OpState
- {
- None,
- GetUnitInfosScheduled,
- GetUnitInfosRunning,
- SaveResultsScheduled,
- SaveResultsRunning,
- }
- ///
- OpState currentOpState;
- bool opCompleted;
- bool anyError;
-
-
- ///
- /// Watermeters to check at the beginning of the cycle
- ///
- IList waterMeters;
-
- ///
- /// Data (tracing records) to write at the end of cycle
- ///
- Results.Entities.Batch batch;
-
-
- public FlexFlow() {}
-
- public FlexFlow(Generic.IComponentCfg cfg)
- : base(cfg)
- {
- flexFlowCfg = cfg as FlexFlowCfg;
- }
-
- public override void Initialize()
- {
- currentOpState = OpState.None;
-
- if (flexFlowCfg.DebugLevel == DebugMode.Normal)
- {
- /// TODO: Initialize FlexFlow
-
- log.FatalFormat("{0} initialized: {1}", Name, this);
- }
- else
- {
- log.FatalFormat("{0} simulated: {1}", Name, this);
- }
- }
-
- ///
- /// IDevice interface implementation
- ///
- public void RunDeviceBefore() { }
- public void RunDeviceAfter() { }
- ///
- public void StopDevice()
- {
- if (flexFlowCfg.DebugLevel != DebugMode.Normal) return;
-
- /// TODO: Close FlexFlow
- }
- public void StopDevice2() {}
-
-
- ///
- /// Reads information on start of a cycle, Events: Event.InfoRead
- ///
- /// Results of water meters
- /// Reference to the operation
- public IOperation ReadStartInfoOp(IList waterMeters)
- {
- if (!flexFlowCfg.CheckPreviousRecords)
- {
- return null;
- }
- else if ((currentOpState == OpState.GetUnitInfosRunning) || (currentOpState == OpState.SaveResultsRunning))
- {
- throw new Exception("Sequence error");
- }
- else
- {
- this.waterMeters = waterMeters;
- currentOpState = OpState.GetUnitInfosScheduled;
- return this;
- }
- }
-
- ///
- /// Writes the test cycle results into a file, Events: Event.ResultsWritten
- ///
- /// Procedure to print the results of
- /// Results to write into the file
- /// Reference to the operation
- public IOperation ProcessResultsOp(Results.Entities.Batch batch)
- {
- if (!flexFlowCfg.SaveResults)
- {
- return null;
- }
- else if ((currentOpState == OpState.GetUnitInfosRunning) || (currentOpState == OpState.SaveResultsRunning))
- {
- throw new Exception("Sequence error");
- }
- else
- {
- this.batch = batch;
- currentOpState = OpState.SaveResultsScheduled;
- return this;
- }
- }
-
-
- /// Start this operation
- public void Start()
- {
- if (currentOpState == OpState.GetUnitInfosScheduled)
- {
- currentOpState = OpState.GetUnitInfosRunning;
- }
- else if (currentOpState == OpState.SaveResultsScheduled)
- {
- currentOpState = OpState.SaveResultsRunning;
- }
-
- opCompleted = false;
- anyError = false;
- }
-
- /// Run this operation
- /// Event.ResultsWritten or Event.Error
- public Event Run()
- {
- if (currentOpState == OpState.GetUnitInfosRunning)
- {
- if (flexFlowCfg.DebugLevel == DebugMode.Simulate)
- {
- return Event.InfoRead;
- }
- else if (!opCompleted)
- {
- log.WarnFormat("{0} : Run() : currentOp = {1}", Name, currentOpState);
- opCompleted = true;
- if (GetUnitInfos(waterMeters) != Retv.OK)
- {
- anyError = true;
- }
- return anyError ? Event.InfoNotRead : Event.InfoRead;
- }
- else
- {
- return anyError ? Event.InfoNotRead : Event.InfoRead;
- }
- }
- else if (currentOpState == OpState.SaveResultsRunning)
- {
- if (flexFlowCfg.DebugLevel == DebugMode.Simulate || batch.WaterMeters.Count == 0)
- {
- return Event.ResultsWritten;
- }
- else if (!opCompleted)
- {
- log.WarnFormat("{0} : Run() : currentOp = {1}", Name, currentOpState);
- opCompleted = true;
- if (SaveResults(batch) != Retv.OK)
- {
- anyError = true;
- }
- return anyError ? Event.ErrorProcessingResults : Event.ResultsWritten;
- }
- else
- {
- return anyError ? Event.ErrorProcessingResults : Event.ResultsWritten;
- }
- }
- else
- {
- return Event.None;
- }
- }
-
- /// Stop this operation
- public void Stop()
- {
- currentOpState = OpState.None;
- }
-
-
- Retv GetUnitInfos(IList waterMeters)
- {
- bool allOK = true;
-
- foreach (var wm in waterMeters)
- {
- if (wm != null && !wm.Disabled)
- {
- GetOneUnitInfo(wm);
- if (wm.LastRecordIsNok)
- {
- allOK = false;
- }
- }
- }
-
- return allOK ? Retv.OK : Retv.Error;
- }
-
- Retv GetOneUnitInfo(Results.Entities.WaterMeter wm)
- {
- if (string.IsNullOrEmpty(wm.SerialNr))
- {
- /// S/N is missing
- wm.LastRecordIsNok = false; /// OK, this is an RFID comm. error, not a production tracing error
- return Retv.OK;
- }
-
- string request;
- if (flexFlowCfg.DoNotSendUserId || string.IsNullOrEmpty(UserID))
- {
- request = '{'
- + string.Format("{0}\"serialNumber\": \"{1}\",{0}\"stationName\": \"{2}\"{0}",
- Environment.NewLine, wm.SerialNr, StationName)
- + '}';
- }
- else
- {
- request = '{'
- + string.Format("{0}\"serialNumber\": \"{1}\",{0}\"stationName\": \"{2}\",{0}\"userId\": \"{3}\"{0}",
- Environment.NewLine, wm.SerialNr, StationName, UserID)
- + '}';
- }
-
- /// TODO: Invoke GetUnitInfo
-
- return Retv.OK;
- }
-
-
- ///
- /// Write results of a batch of water meters to the DB
- ///
- /// Batch entity
- Retv SaveResults(Results.Entities.Batch batch)
- {
- try
- {
- int writtenRecordsCount = 0;
- foreach (var wMtr in batch.WaterMeters)
- {
- if (wMtr.SessionId == 1)
- {
- writtenRecordsCount += SaveOneUnitResult(wMtr);
- }
- }
-
- log.ErrorFormat("Batch {0}: {1} of {2} watermeters written to the regular production tracing DB",
- batch.BatchNr, writtenRecordsCount, batch.WaterMeters.Count);
- }
- catch (Exception exc)
- {
- log.WarnFormat("Failed to write results of batch {0} to production tracing DB: {1}",
- batch.BatchNr, exc.Message);
- return Retv.Error;
- }
-
- return Retv.OK;
- }
-
-
- ///
- /// Write one meter results to the DB
- ///
- /// Watermeter entity
- /// Number of written water meters (0 or 1)
- int SaveOneUnitResult(Results.Entities.WaterMeter wm)
- {
- if (string.IsNullOrEmpty(wm.SerialNr) || (wm.ProcessId == 0))
- {
- /// Without PCB number there is no DB activity
- return 0;
- }
- else
- {
- /// TODO: Invoke SaveResult
- return 1;
- }
- }
- }
-}
diff --git a/TBF/BenchControl/Output/FlexFlow/Rest/FlexFlowCfg.cs b/TBF/BenchControl/Output/FlexFlow/Rest/FlexFlowCfg.cs
deleted file mode 100644
index 0889b83fd..000000000
--- a/TBF/BenchControl/Output/FlexFlow/Rest/FlexFlowCfg.cs
+++ /dev/null
@@ -1,172 +0,0 @@
-///
-/// Copyright (c) 2021 Sensus Slovensko a.s.
-///
-using System;
-using System.Collections.Generic;
-using System.Xml.Serialization;
-using Config.Entities;
-using TBF.BenchControl.Generic;
-using TBF.Resources;
-
-namespace TBF.BenchControl.Output.FlexFlow.Rest
-{
- ///
- /// Holds FlexFlow.Rest configuration.
- ///
- public class FlexFlowCfg : ComponentCfgBase, Generic.IComponentCfg, IParamsProvider
- {
- public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(FlexFlowCfg) })[0];
- public override XmlSerializer GetSerializer() { return Serializer; }
-
- public IComponentCfgCtrl GetControl(IList cmpntEntities) { return new Configs.ParamsProvider.ComponentCfgCtrl(this, null); }
-
- ///
- /// Serialized parameters
- ///
- public string Url;
- public string Url2;
- public bool CheckPreviousRecords;
- public bool SaveResults;
- public bool DoNotSendUserId;
-
-
- /// Private parameterless constructor invoked by all other (public) constructors
- FlexFlowCfg() { }
-
- public FlexFlowCfg(string name, IComponentFactory factory)
- : this()
- {
- Name = name;
- Factory = factory;
- ParentName = string.Empty;
- InitializeAll();
- }
-
- public string ComponentName { get { return Name; } }
-
- public void InitializeAll()
- {
- Url = "http://gdlm02205:4450/?wsdl";
- Url2 = string.Empty;
- CheckPreviousRecords = true;
- SaveResults = true;
- DoNotSendUserId = true;
- }
-
- string[] paramNames = new string[]
- {
- "URL",
- "URL2",
- "Get Unit Info",
- "Save Result",
- "Do not send UserId",
- };
- public string ParamName(int i) { return paramNames[i]; }
- public int ParamsCount() { return paramNames.Length; }
-
- public ICollection ParamValues(int i)
- {
- switch (i)
- {
- case 2:
- case 3:
- case 4:
- return new List() { Strings.yes, Strings.no };
- default:
- return null;
- }
- }
-
- public string ToString(int i)
- {
- switch (i)
- {
- case 0:
- return Url;
- case 1:
- return Url2;
- case 2:
- return CheckPreviousRecords ? Strings.yes : Strings.no;
- case 3:
- return SaveResults ? Strings.yes : Strings.no;
- case 4:
- return DoNotSendUserId ? Strings.yes : Strings.no;
- default:
- return string.Format("{0} CheckPrev.={1}, SaveResults={2}, URL={3}, URL2={4}",
- Name,
- CheckPreviousRecords,
- SaveResults,
- Url,
- Url2);
- }
- }
-
- public CfgUpdateFlags UpdateParam(int i, string strValue)
- {
- switch (i)
- {
- case 0:
- Url = strValue;
- return CfgUpdateFlags.RestartRqrd;
- case 1:
- Url2 = strValue;
- return CfgUpdateFlags.RestartRqrd;
- case 2:
- CheckPreviousRecords = (strValue == Strings.yes);
- return CfgUpdateFlags.RestartRqrd;
- case 3:
- SaveResults = (strValue == Strings.yes);
- return CfgUpdateFlags.RestartRqrd;
- case 4:
- DoNotSendUserId = (strValue == Strings.yes);
- return CfgUpdateFlags.RestartRqrd;
- default:
- return CfgUpdateFlags.None;
- }
- }
-
- public bool ValidateParam(int i, string strValue, out string message)
- {
- message = string.Empty;
-
- switch (i)
- {
- case 0:
- case 1:
- return true;
- case 2:
- case 3:
- case 4:
- if (ParamValues(i).Contains(strValue)) return true;
- break;
- default:
- message = "Invalid index";
- return false;
- }
-
- message = ParamName(i) + " is invalid";
- return false;
- }
-
- void CopyContentTo(FlexFlowCfg prms)
- {
- prms.Url = this.Url;
- prms.Url2 = this.Url2;
- prms.CheckPreviousRecords = this.CheckPreviousRecords;
- prms.SaveResults = this.SaveResults;
- prms.DoNotSendUserId = this.DoNotSendUserId;
- }
-
- public Config.Entities.IParamsProvider Clone()
- {
- FlexFlowCfg pars = new FlexFlowCfg();
- CopyContentTo(pars);
- return pars;
- }
-
- public bool UpdateEmbeddedDbEntity()
- {
- return true; /// =OK, do nothing
- }
- }
-}
diff --git a/TBF/BenchControl/Output/FlexFlow/ResultsData.cs b/TBF/BenchControl/Output/FlexFlow/ResultsData.cs
new file mode 100644
index 000000000..881448e6a
--- /dev/null
+++ b/TBF/BenchControl/Output/FlexFlow/ResultsData.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace TBF.BenchControl.Output.FlexFlow
+{
+ public class ResultsData
+ {
+ public string SerialNr;
+ public string StationName;
+ public string UserId;
+
+ public int Id;
+
+ public ResultsData(string serialNr, string stationName, string userId)
+ {
+ SerialNr = serialNr;
+ StationName = stationName;
+ UserId = userId;
+
+ Id = 9999;
+ }
+ }
+}
diff --git a/TBF/BenchControl/Output/FlexFlow/UnitInfoData.cs b/TBF/BenchControl/Output/FlexFlow/UnitInfoData.cs
new file mode 100644
index 000000000..88ac938ac
--- /dev/null
+++ b/TBF/BenchControl/Output/FlexFlow/UnitInfoData.cs
@@ -0,0 +1,27 @@
+using System;
+
+namespace TBF.BenchControl.Output.FlexFlow
+{
+ public class UnitInfoData
+ {
+ public string SerialNr;
+ public string StationName;
+ public string UserId;
+
+ public int Id;
+ public string Value;
+ public long FFUnitId;
+ public long FFTestId;
+ public long FFPartId;
+
+ public UnitInfoData(string serialNr, string stationName, string userId)
+ {
+ SerialNr = serialNr;
+ StationName = stationName;
+ UserId = userId;
+
+ Id = 9999;
+ Value = string.Empty;
+ }
+ }
+}
diff --git a/TBF/BenchControl/Sequences/SequenceBase.cs b/TBF/BenchControl/Sequences/SequenceBase.cs
index 3993c395e..c7942799c 100644
--- a/TBF/BenchControl/Sequences/SequenceBase.cs
+++ b/TBF/BenchControl/Sequences/SequenceBase.cs
@@ -1599,7 +1599,7 @@ namespace TBF.BenchControl.Sequences
for (int i = 0; i < BatchRslts.WMPositionsCount; i++)
{
- float errorPct = 0;
+ double errorPct = Convert.ToDouble((i % 10) - 5) / 2.0;
Results.Entities.MeterTestRslt meterRslt = ProcessData.BatchRslts.GetMeterTestRslt(fullTestName, i, CompoundMeterId.Single);
IRegReader regReader = sensPath.RegisterReaders[i];
@@ -1618,7 +1618,7 @@ namespace TBF.BenchControl.Sequences
meterRslt.TimestampEnd = tstRslt.TargetTime();
meterRslt.TestTime = tstRslt.TargetTime();
meterRslt.Error = errorPct;
- meterRslt.Passed = true;
+ meterRslt.Passed = errorPct >= test.ErrLimLo + test.Uncertainty && errorPct <= test.ErrLimHi - test.Uncertainty;
meterRslt.TestDone = true;
tstRslt.TestDone = true;
}
diff --git a/TBF/BenchControl/TbfComponents.cs b/TBF/BenchControl/TbfComponents.cs
index 47cd84b1b..9b4ba5340 100644
--- a/TBF/BenchControl/TbfComponents.cs
+++ b/TBF/BenchControl/TbfComponents.cs
@@ -155,7 +155,7 @@ namespace TBF.BenchControl
Factories.Add(new Output.FileWriters.Xml.FactoryCompound());
Factories.Add(new Output.FileWriters.Xml.FactoryHeatMeters());
Factories.Add(new Output.FileWriters.XmlBatch.FactorySingle());
- Factories.Add(new Output.FlexFlow.Rest.Factory());
+ Factories.Add(new Output.FlexFlow.Factory());
Factories.Add(new Output.Printers.Enhanced.FactorySingle());
Factories.Add(new Output.Printers.Enhanced.FactoryCompound());
Factories.Add(new Output.Printers.Enhanced.FactoryHeatMeters());
diff --git a/TBF/TBF.csproj b/TBF/TBF.csproj
index a9b80174a..4e9c3aa72 100644
--- a/TBF/TBF.csproj
+++ b/TBF/TBF.csproj
@@ -141,6 +141,7 @@
+
@@ -1107,9 +1108,11 @@
WriterCfgCtrl.cs
-
-
-
+
+
+
+
+
Form