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