diff --git a/Results/Entities/WaterMeter.cs b/Results/Entities/WaterMeter.cs index a186b85c3..5f8fe92ce 100644 --- a/Results/Entities/WaterMeter.cs +++ b/Results/Entities/WaterMeter.cs @@ -91,12 +91,15 @@ namespace Results.Entities public virtual int ProdQ2CorrLR { get; set; } /// iPerl Q2 correction for the L-flow written to iPerl #endif #if ORACLE_DB - public virtual int Pruefindex { get; set; } /// Not mapped to DB !!! 0=unknown (saving to Oracle failed), >=1 ... pruefindex - public virtual int HydrPruefung { get; set; } /// Not mapped to DB !!! + public virtual int Pruefindex { get; set; } /// >= 1 ... pruefindex + public virtual int HydrPruefung { get; set; } public virtual int WMTypeRevision { get; set; } /// Not mapped to DB !!! public virtual object TestInfos { get; set; } /// SensusTestInfo[] array fetched at the beginning of the cycle, not mapped to DB !!! #endif - public virtual WaterMeterData WaterMeterData { get; set; } + public virtual int ProcessId { get; set; } /// Not mapped to DB !!! Tracing DB Process ID + public virtual bool IsLastRecordOk { get; set; } /// Not mapped to DB !!! Previous record verification result + + public virtual WaterMeterData WaterMeterData { get; set; } public virtual Batch Batch { get; set; } public virtual IList MeterTestRslts { get; set; } @@ -354,6 +357,8 @@ namespace Results.Entities WMTypeRevision = 0; TestInfos = null; #endif + ProcessId = 0; + IsLastRecordOk = false; } @@ -410,6 +415,9 @@ namespace Results.Entities WMTypeRevision = src.WMTypeRevision; TestInfos = src.TestInfos; #endif + ProcessId = src.ProcessId; + IsLastRecordOk = src.IsLastRecordOk; + foreach (var mtr in MeterTestRslts) { MeterTestRslt srcMtr = src.GetMeterTestRslt(mtr.Name(), (Config.Entities.CompoundMeterId)mtr.CompoundMeterId); diff --git a/Results/Properties/AssemblyInfo.cs b/Results/Properties/AssemblyInfo.cs index a0697151c..ec544ff80 100644 --- a/Results/Properties/AssemblyInfo.cs +++ b/Results/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Sensus")] [assembly: AssemblyProduct("Results")] -[assembly: AssemblyCopyright("Copyright © 2013 - 2018 Sensus Slovensko, a.s.")] +[assembly: AssemblyCopyright("Copyright © 2013 - 2019 Sensus Slovensko, a.s.")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.18.1142.0")] -[assembly: AssemblyFileVersion("2.18.1142.0")] +[assembly: AssemblyVersion("2.18.1161.0")] +[assembly: AssemblyFileVersion("2.18.1161.0")] diff --git a/TBF/BenchControl/Output/DB/ProductionTracing/MonitoringCfg.cs b/TBF/BenchControl/Output/DB/ProductionTracing/MonitoringCfg.cs index 09d12242d..e76a830fc 100644 --- a/TBF/BenchControl/Output/DB/ProductionTracing/MonitoringCfg.cs +++ b/TBF/BenchControl/Output/DB/ProductionTracing/MonitoringCfg.cs @@ -19,6 +19,8 @@ namespace TBF.BenchControl.Output.DB.ProductionTracing public string Workplace; public string ConnStr; + public bool CheckPreviousRecords; + public bool SaveTracingRecords; /// Private parameterless constructor invoked by all other (public) constructors @@ -27,7 +29,9 @@ namespace TBF.BenchControl.Output.DB.ProductionTracing ParentName = string.Empty; Workplace = "WR10"; ConnStr = "SERVER=10.42.128.16; DATABASE=st_wr_shared_config; UID=u9305784; PASSWORD=p89344390; CHARSET=utf8;"; - } + CheckPreviousRecords = true; + SaveTracingRecords = true; + } public MonitoringCfg(string name, IComponentFactory factory) : this() @@ -38,7 +42,12 @@ namespace TBF.BenchControl.Output.DB.ProductionTracing public string ToString(int i) { - return string.Format("Name={0}, Workplace={1}", Name, Workplace); + return string.Format("Name={0}, Workplace={1}, CheckPreviousRecords={2}, SaveTracingRecords={3}, ConnStr={4}", + Name, + Workplace, + CheckPreviousRecords, + SaveTracingRecords, + ConnStr); } } } diff --git a/TBF/BenchControl/Output/DB/ProductionTracing/Tracing.cs b/TBF/BenchControl/Output/DB/ProductionTracing/Tracing.cs index 04b7cf2b3..f160cc4e9 100644 --- a/TBF/BenchControl/Output/DB/ProductionTracing/Tracing.cs +++ b/TBF/BenchControl/Output/DB/ProductionTracing/Tracing.cs @@ -1,10 +1,12 @@ /// -/// Copyright (c) 2018 Sensus Slovensko a.s. +/// Copyright (c) 2018-2019 Sensus Slovensko a.s. /// using System; using System.Collections.Generic; +using System.Linq; using log4net; using NHibernate; +using NHibernate.Criterion; using Config.Entities; using TracingDB.Entities; @@ -16,37 +18,61 @@ namespace TBF.BenchControl.Output.DB.ProductionTracing Error, } - public class Tracing : ComponentBase, IOperation, GenericDevices.IResultsWriter, Generic.IDevice + class DateTimeComparer : IComparer + { + public int Compare(DateTime x, DateTime y) + { + return DateTime.Compare(x, y); + } + } + + public class Tracing : ComponentBase, IOperation, GenericDevices.IResultsWriter, GenericDevices.IStartInfoReader, Generic.IDevice { private static readonly ILog log = LogManager.GetLogger(typeof(Tracing)); public override string ToString() { return string.Format("Tracing({0})", Cfg.ToString(1)); } - public const string WorkstepName = "Test_bench"; - MonitoringCfg tracingCfg; string workplace { get { return tracingCfg.Workplace; } } + enum CurrentOp { None, - SaveRecords, + CheckPreviousRecords, + SaveTracingRecords, } - + /// CurrentOp currentOp; bool opCompleted; bool anyError; + + /// + /// Watermeters to check at the beginning of the cycle + /// + Results.Entities.WaterMeter[] waterMeters; + /// - /// Results to print + /// Data (tracing records) to write at the end of cycle /// Results.Entities.Batch batch; - ISessionFactory sessionFactory; /// Factory to create database sessions initialized in the constructor + + ISessionFactory sessionFactory; /// Factory to create database sessions that is initialized in the constructor IList processes; Dictionary processDictionary; Dictionary> workstepsDictionary; + + /// + /// Previous workstep verification info + /// + Process currentProcess; /// Currently used process + Workstep currentWorkstep; /// Currently used workstep of this test bench + Workstep verifiedWorkstep; + Part verifiedPart; /// Applicable when verifyReferencePart == false + bool verifyReferencePart; public Tracing() {} @@ -67,36 +93,33 @@ namespace TBF.BenchControl.Output.DB.ProductionTracing /// /// Session factory is used to create database sessions /// - sessionFactory = FluentNHibernate.Cfg.Fluently - .Configure() - .Database(FluentNHibernate.Cfg.Db.MySQLConfiguration.Standard.ConnectionString(tracingCfg.ConnStr)) - .Mappings(m => m.FluentMappings.AddFromAssemblyOf()) - .ExposeConfiguration(TracingDB.DB.BuildSchema) - .BuildSessionFactory(); + sessionFactory = FluentNHibernate.Cfg.Fluently.Configure() + .Database(FluentNHibernate.Cfg.Db.MySQLConfiguration.Standard.ConnectionString(tracingCfg.ConnStr)) + .Mappings(m => m.FluentMappings.AddFromAssemblyOf()) + .ExposeConfiguration(TracingDB.DB.BuildSchema) + .BuildSessionFactory(); + /// + /// Register this test bench in the tracing DB for approx. 2 weeks + /// + ISession session = sessionFactory.OpenSession(); - using (ISession session = sessionFactory.OpenSession()) - { - /// - /// Read processes and worksteps from the database (this verifies DB connection as well) - /// - log.WarnFormat("Reading processes/worksteps from production tracing DB started"); - ReadProcessesFromDB(session); - log.WarnFormat("Reading processes/worksteps from production tracing DB completed"); + TracingDB.DB.RegisterWorkplace(session, + workplace, + Users.GlobalData.GetCurrentUserName(), + "1.2.3.4", + "", + WorkstepName, + DateTime.Now + new TimeSpan(15, 0, 0, 0)); + session.Flush(); + session.Close(); + if (session != null) session.Dispose(); - /// - /// Register the test bench workplace - /// - TracingDB.DB.RegisterWorkplace(session, - workplace, - Users.GlobalData.GetCurrentUserName(), - "1.2.3.4", - "", - WorkstepName, - DateTime.Now + new TimeSpan(15, 0, 0, 0)); /// Test Bench registered for approx. 2. weeks - session.Flush(); - } + currentProcess = null; } - + /// + public void RunDeviceBefore() { } + public void RunDeviceAfter() { } + /// public void StopDevice() { if (tracingCfg.DebugLevel == DebugMode.Simulate) return; @@ -107,13 +130,31 @@ namespace TBF.BenchControl.Output.DB.ProductionTracing session.Flush(); } } - public void StopDevice2() {} - public void RunDeviceBefore() {} - public void RunDeviceAfter() {} - + /// + /// Reads information on start of a cycle, Events: Event.InfoRead + /// + /// Results of water meters + /// Reference to the operation + public IOperation ReadStartInfoOp(Results.Entities.WaterMeter[] waterMeters) + { + if (!tracingCfg.CheckPreviousRecords) + { + return null; + } + else if (currentOp != CurrentOp.None) + { + throw new Exception("Sequence error"); + } + else + { + this.waterMeters = waterMeters; + currentOp = CurrentOp.CheckPreviousRecords; + return this; + } + } /// /// Writes the test cycle results into a file, Events: Event.ResultsWritten @@ -123,16 +164,23 @@ namespace TBF.BenchControl.Output.DB.ProductionTracing /// Reference to the operation public IOperation WriteResultsOp(Results.Entities.Batch batch) { - if (currentOp != CurrentOp.None) + if (!tracingCfg.SaveTracingRecords) + { + return null; + } + else if (currentOp != CurrentOp.None) + { throw new Exception("Sequence error"); + } else - currentOp = CurrentOp.SaveRecords; - - this.batch = batch; - - return this; + { + this.batch = batch; + currentOp = CurrentOp.SaveTracingRecords; + return this; + } } + /// Start this operation public void Start() { @@ -144,50 +192,47 @@ namespace TBF.BenchControl.Output.DB.ProductionTracing /// Event.ResultsWritten or Event.Error public Event Run() { - if (tracingCfg.DebugLevel == DebugMode.Simulate || batch.WaterMeters.Count == 0) + if (currentOp == CurrentOp.CheckPreviousRecords) { - opCompleted = true; - return Event.ResultsWritten; - } - - if (currentOp == CurrentOp.SaveRecords) - { - if (!opCompleted) + if (tracingCfg.DebugLevel == DebugMode.Simulate) { - ITransaction transaction = null; - try - { - ISession session = sessionFactory.OpenSession(); - transaction = session.BeginTransaction(); - - int wrCount = WriteResultsToDatabase(session, batch); - transaction.Commit(); - session.Flush(); - opCompleted = true; - - log.ErrorFormat("Batch {0}: {1} of {2} watermeters written to production tracing DB", - batch.BatchNr, wrCount, batch.WaterMeters.Count); - } - catch (Exception exc) - { - if (transaction != null && !transaction.WasCommitted) transaction.Rollback(); - anyError = true; - - log.WarnFormat("Failed to write results of batch {0} to production tracing DB: {1}", - batch.BatchNr, exc.Message); - } + return Event.InfoRead; + } + else if (!opCompleted) + { + log.WarnFormat("{0} : Run() : currentOp = {1}", Name, currentOp); + opCompleted = true; + if (CheckPreviousRecords(waterMeters) != Retv.OK) anyError = true; + return anyError ? Event.InfoNotRead : Event.InfoRead; } - - if (anyError) - return Event.ResultsNotWritten; else + { + return anyError ? Event.InfoNotRead : Event.InfoRead; + } + } + else if (currentOp == CurrentOp.SaveTracingRecords) + { + if (tracingCfg.DebugLevel == DebugMode.Simulate || batch.WaterMeters.Count == 0) + { return Event.ResultsWritten; + } + else if (!opCompleted) + { + log.WarnFormat("{0} : Run() : currentOp = {1}", Name, currentOp); + opCompleted = true; + if (SaveTracingRecords(batch) != Retv.OK) anyError = true; + return anyError ? Event.ResultsNotWritten : Event.ResultsWritten; + } + else + { + return anyError ? Event.ResultsNotWritten : Event.ResultsWritten; + } } else { return Event.None; } - } + } /// Stop this operation public void Stop() @@ -196,19 +241,170 @@ namespace TBF.BenchControl.Output.DB.ProductionTracing } + Retv CheckPreviousRecords(Results.Entities.WaterMeter[] waterMeters) + { + Retv retVal = Retv.Error; + + using (ISession session = sessionFactory.OpenSession()) + { + for (int i = 0; i < waterMeters.Length; i++) + { + Results.Entities.WaterMeter wm = waterMeters[i]; + + if ((wm != null) && !string.IsNullOrEmpty(wm.SerialNr)) + { + CheckPreviousRecordOfSingleWM(session, wm); + } + } + + retVal = Retv.OK; + } + + return retVal; + } + + Retv CheckPreviousRecordOfSingleWM(ISession session, Results.Entities.WaterMeter wm) + { + /// + /// Get all already existing reference records with Code == wm.SerialNr, refRecords[0] will be the most recent one + /// + ReferenceRecord refRecord = null; + Workstep workstep = null; + + ProjectionList projections = Projections.ProjectionList(); + projections.Add(Projections.Property(() => refRecord.Id)); /// rslt[0] = reference record ID + projections.Add(Projections.Property(() => workstep.Id)); /// rslt[1] = workstep ID + projections.Add(Projections.Property(() => refRecord.Process.Id)); /// rslt[2] = process ID + projections.Add(Projections.Property(() => refRecord.TimeStamp)); /// rslt[3] = reference record time stamp + + /// + /// Get all reference records with Code == SerialNr from all worksteps different from 'Test_bench' + /// + IList results = session.QueryOver(() => refRecord) + .Where(rr => (rr.Code == wm.SerialNr)) + .JoinQueryOver(rr => rr.Workstep, () => workstep) + .And(ws => (ws.Name != WorkstepName)) + .Select(projections) + .List(); + + if (results.Count == 0) + { + /// No records found => IsLastRecordOk = false + wm.ProcessId = 0; + wm.IsLastRecordOk = false; + return Retv.OK; + } + + /// + /// Get processId of the last record + /// + int processId = 0; + DateTime lastTimeStamp = DateTime.MinValue; + foreach (var rslt in results) + { + if (DateTime.Compare((DateTime)rslt[3], lastTimeStamp) > 0) + { + lastTimeStamp = (DateTime)rslt[3]; + processId = (int)rslt[2]; + } + } + + if (processId == 0) return Retv.Error; /// This should never happen + + + if ((currentProcess == null) || (currentProcess.Id != processId)) + { + /// + /// Process changed ==> update currentProcess / currentWorkstep / verifiedWorkstep / verifiedPart / verifyReferencePart + /// + var processes = session.QueryOver() + .Where(p => (p.Id == processId)) + .List(); + + if (processes.Count != 1) return Retv.Error; + + IList worksteps = session.QueryOver() + .Where(ws => (ws.Process == processes[0])) + .And( ws => (ws.Name == WorkstepName)) + .List(); + + if (worksteps.Count != 1) return Retv.Error; + + Workstep vWS; + Part vP; + bool vRP; + /// + if (!TracingDB.DB.AnalyzeProcess(session, processes[0], worksteps[0], out vWS, out vRP, out vP)) + { + return Retv.Error; /// Unable to + } + + currentProcess = processes[0]; + currentWorkstep = worksteps[0]; + verifiedWorkstep = vWS; + verifiedPart = vP; + verifyReferencePart = vRP; + } + + wm.ProcessId = currentProcess.Id; + wm.IsLastRecordOk = false; + foreach (var rslt in results) + { + if ((verifiedWorkstep.Id == (int)rslt[1]) && (processId == (int)rslt[2])) + { + wm.IsLastRecordOk = true; + break; + } + } + + return Retv.OK; + } + + /// /// Write results of a batch of water meters to the DB /// /// DB session /// Batch entity - int WriteResultsToDatabase(ISession session, Results.Entities.Batch batch) + Retv SaveTracingRecords(Results.Entities.Batch batch) { - int wrCount = 0; - foreach (var wMtr in batch.WaterMeters) - { - wrCount += SaveSingleWM2DB(session, wMtr); - } - return wrCount; + ITransaction transaction = null; + ISession session = null; + try + { + session = sessionFactory.OpenSession(); + transaction = session.BeginTransaction(); + + int writtenRecordsCount = 0; + foreach (var wMtr in batch.WaterMeters) + { + writtenRecordsCount += SaveSingleWM2DB(session, wMtr); + } + transaction.Commit(); + session.Flush(); + + log.ErrorFormat("Batch {0}: {1} of {2} watermeters written to production tracing DB", + batch.BatchNr, writtenRecordsCount, batch.WaterMeters.Count); + + return Retv.OK; + } + catch (Exception exc) + { + if (transaction != null && !transaction.WasCommitted) transaction.Rollback(); + + log.WarnFormat("Failed to write results of batch {0} to production tracing DB: {1}", + batch.BatchNr, exc.Message); + + return Retv.Error; + } + finally + { + if (session != null) + { + session.Close(); + session.Dispose(); + } + } } @@ -220,36 +416,67 @@ namespace TBF.BenchControl.Output.DB.ProductionTracing /// Number of written reference record int SaveSingleWM2DB(ISession session, Results.Entities.WaterMeter wm) { - if (string.IsNullOrEmpty(wm.SerialNr)) return 0; /// Without PCB number there is no DB activity - - /// - /// Get all already existing reference records with Code == wm.SerialNr, refRecords[0] will be the most recent one - /// - IList refRecords; - int trialsCount = 0; - do + if (string.IsNullOrEmpty(wm.SerialNr)) { - if (trialsCount > 0) ReadProcessesFromDB(session); - - refRecords = session.QueryOver() - .Where(rr => (rr.Code == wm.SerialNr)) - .OrderBy(rr => rr.TimeStamp).Desc - .List(); - trialsCount++; + /// Without PCB number there is no DB activity + return 0; } - while (refRecords.Count == 0 && trialsCount <= 2); - - /// - /// Save a new reference record from this test bench if workstep "Test_bench" is defined in the obtained WM process - /// - IList worksteps; - if ((refRecords.Count > 0) && workstepsDictionary.TryGetValue(refRecords[0].Process.Id, out worksteps) && (worksteps.Count == 1)) + else if ((currentProcess != null) && (wm.ProcessId == currentProcess.Id)) { - session.SaveOrUpdate(new ReferenceRecord(refRecords[0].Process, worksteps[0], wm.SerialNr, Users.GlobalData.CurrentUser.UserName, workplace, wm.Passed ? 0 : 1)); + /// Process is already loaded => save a reference record for this WM + session.SaveOrUpdate(new ReferenceRecord(currentProcess, currentWorkstep, wm.SerialNr, Users.GlobalData.CurrentUser.UserName, workplace, wm.Passed ? 0 : 1)); return 1; } + else if (wm.ProcessId != 0) + { + /// Process ID is already known, however process and workstep have to be loaded + IList processes = session.QueryOver() + .Where(x => (x.Id == wm.ProcessId)) + .List(); + if (processes.Count != 1) return 0; - return 0; + foreach (var ws in processes[0].Worksteps) + { + if (ws.Name == WorkstepName) + { + session.SaveOrUpdate(new ReferenceRecord(processes[0], ws, wm.SerialNr, Users.GlobalData.CurrentUser.UserName, workplace, wm.Passed ? 0 : 1)); + return 1; + } + } + + return 0; /// No workste with 'WorkstepName' found + } + else + { + /// + /// Process ID is unknown => do everything from scratch + /// + + /// Get all already existing reference records with Code == wm.SerialNr, refRecords[0] will be the most recent one + IList refRecords; + int trialsCount = 0; + do + { + if (trialsCount > 0) ReadProcessesFromDB(session); + + refRecords = session.QueryOver() + .Where(rr => (rr.Code == wm.SerialNr)) + .OrderBy(rr => rr.TimeStamp).Desc + .List(); + trialsCount++; + } + while (refRecords.Count == 0 && trialsCount <= 2); + + /// Save a new reference record from this test bench if workstep "Test_bench" is defined in the obtained WM process + IList worksteps; + if ((refRecords.Count > 0) && workstepsDictionary.TryGetValue(refRecords[0].Process.Id, out worksteps) && (worksteps.Count == 1)) + { + session.SaveOrUpdate(new ReferenceRecord(refRecords[0].Process, worksteps[0], wm.SerialNr, Users.GlobalData.CurrentUser.UserName, workplace, wm.Passed ? 0 : 1)); + return 1; + } + + return 0; + } } @@ -259,12 +486,17 @@ namespace TBF.BenchControl.Output.DB.ProductionTracing /// DB session void ReadProcessesFromDB(ISession session) { - processes = session.QueryOver().List(); + processes = session.QueryOver() + .List(); + processDictionary = new Dictionary(); workstepsDictionary = new Dictionary>(); + /// foreach (var pr in processes) { - IList worksteps = session.QueryOver().Where(ws => ((ws.Process == pr) && (ws.Name == WorkstepName))).List(); + IList worksteps = session.QueryOver() + .Where(ws => ((ws.Process == pr) && (ws.Name == WorkstepName))) + .List(); processDictionary.Add(pr.Id, pr); workstepsDictionary.Add(pr.Id, worksteps); } diff --git a/TBF/BenchControl/Output/DB/ProductionTracing/TracingCfgCtrl.cs b/TBF/BenchControl/Output/DB/ProductionTracing/TracingCfgCtrl.cs index 38b573ac2..f2fb80369 100644 --- a/TBF/BenchControl/Output/DB/ProductionTracing/TracingCfgCtrl.cs +++ b/TBF/BenchControl/Output/DB/ProductionTracing/TracingCfgCtrl.cs @@ -45,14 +45,18 @@ namespace TBF.BenchControl.Output.DB.ProductionTracing nameTextBox.Text = config.Name; workplaceTextBox.Text = config.Workplace; connStrTextBox.Text = config.ConnStr; - } + checkPreviousRecordsCheckBox.Checked = config.CheckPreviousRecords; + saveTracingRecordsCheckBox.Checked = config.SaveTracingRecords; + } public void Unlock() { nameTextBox.Enabled = true; workplaceTextBox.Enabled = true; connStrTextBox.Enabled = true; - } + checkPreviousRecordsCheckBox.Enabled = true; + saveTracingRecordsCheckBox.Enabled = true; + } public CfgUpdateFlags VerifyCfg(ref string message) { @@ -68,11 +72,13 @@ namespace TBF.BenchControl.Output.DB.ProductionTracing if (config.Name != nameTextBox.Text) { config.Name = nameTextBox.Text; - flags |= CfgUpdateFlags.AnyChange | CfgUpdateFlags.RestartRqrd; + flags |= (CfgUpdateFlags.AnyChange | CfgUpdateFlags.RestartRqrd); } flags |= UpdateDifferent(ref config.Workplace, workplaceTextBox.Text, CfgUpdateFlags.AnyChange | CfgUpdateFlags.RestartRqrd); flags |= UpdateDifferent(ref config.ConnStr, connStrTextBox.Text, CfgUpdateFlags.AnyChange | CfgUpdateFlags.RestartRqrd); + flags |= UpdateDifferent(ref config.CheckPreviousRecords, checkPreviousRecordsCheckBox.Checked, CfgUpdateFlags.AnyChange | CfgUpdateFlags.RestartRqrd); + flags |= UpdateDifferent(ref config.SaveTracingRecords, saveTracingRecordsCheckBox.Checked, CfgUpdateFlags.AnyChange | CfgUpdateFlags.RestartRqrd); return flags; } diff --git a/TBF/BenchControl/Output/DB/ProductionTracing/TracingCfgCtrl.designer.cs b/TBF/BenchControl/Output/DB/ProductionTracing/TracingCfgCtrl.designer.cs index e0901c6a2..a42862286 100644 --- a/TBF/BenchControl/Output/DB/ProductionTracing/TracingCfgCtrl.designer.cs +++ b/TBF/BenchControl/Output/DB/ProductionTracing/TracingCfgCtrl.designer.cs @@ -38,6 +38,8 @@ namespace TBF.BenchControl.Output.DB.ProductionTracing this.workplaceLabel = new System.Windows.Forms.Label(); this.connStrTextBox = new System.Windows.Forms.TextBox(); this.connStrLabel = new System.Windows.Forms.Label(); + this.saveTracingRecordsCheckBox = new System.Windows.Forms.CheckBox(); + this.checkPreviousRecordsCheckBox = new System.Windows.Forms.CheckBox(); this.SuspendLayout(); // // nameTextBox @@ -100,10 +102,34 @@ namespace TBF.BenchControl.Output.DB.ProductionTracing this.connStrLabel.TabIndex = 21; this.connStrLabel.Text = "Connection string"; // + // saveTracingRecordsCheckBox + // + this.saveTracingRecordsCheckBox.AutoSize = true; + this.saveTracingRecordsCheckBox.Enabled = false; + this.saveTracingRecordsCheckBox.Location = new System.Drawing.Point(110, 154); + this.saveTracingRecordsCheckBox.Name = "saveTracingRecordsCheckBox"; + this.saveTracingRecordsCheckBox.Size = new System.Drawing.Size(177, 17); + this.saveTracingRecordsCheckBox.TabIndex = 24; + this.saveTracingRecordsCheckBox.Text = "Save production tracing records"; + this.saveTracingRecordsCheckBox.UseVisualStyleBackColor = true; + // + // checkPreviousRecordsCheckBox + // + this.checkPreviousRecordsCheckBox.AutoSize = true; + this.checkPreviousRecordsCheckBox.Enabled = false; + this.checkPreviousRecordsCheckBox.Location = new System.Drawing.Point(110, 131); + this.checkPreviousRecordsCheckBox.Name = "checkPreviousRecordsCheckBox"; + this.checkPreviousRecordsCheckBox.Size = new System.Drawing.Size(138, 17); + this.checkPreviousRecordsCheckBox.TabIndex = 23; + this.checkPreviousRecordsCheckBox.Text = "Check previous records"; + this.checkPreviousRecordsCheckBox.UseVisualStyleBackColor = true; + // // TracingCfgCtrl // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.saveTracingRecordsCheckBox); + this.Controls.Add(this.checkPreviousRecordsCheckBox); this.Controls.Add(this.connStrTextBox); this.Controls.Add(this.connStrLabel); this.Controls.Add(this.workplaceTextBox); @@ -128,5 +154,7 @@ namespace TBF.BenchControl.Output.DB.ProductionTracing private System.Windows.Forms.Label workplaceLabel; private System.Windows.Forms.TextBox connStrTextBox; private System.Windows.Forms.Label connStrLabel; + private System.Windows.Forms.CheckBox saveTracingRecordsCheckBox; + private System.Windows.Forms.CheckBox checkPreviousRecordsCheckBox; } } diff --git a/TBF/BenchControl/Sequences/MainSeq.cs b/TBF/BenchControl/Sequences/MainSeq.cs index 1dac9ea5c..c82aea1ab 100644 --- a/TBF/BenchControl/Sequences/MainSeq.cs +++ b/TBF/BenchControl/Sequences/MainSeq.cs @@ -590,8 +590,10 @@ namespace TBF.BenchControl.Sequences /// if ((simultWithPurgingCount > 0) || (readingStartInfo.Operations.Count > 0)) { - bool completed = !(modelessDlg is GenericDevices.IHasCompleted) - || (modelessDlg as GenericDevices.IHasCompleted).Completed; + /// Get 'bool completed' + bool completed = !(modelessDlg is GenericDevices.IHasCompleted) || (modelessDlg as GenericDevices.IHasCompleted).Completed; + if (lastDataEntryCmpnt != null) completed = completed && lastDataEntryCmpnt.Completed; + if (!completed) { State.Create("MainSeq : Wait until the entry form is closed") @@ -606,7 +608,9 @@ namespace TBF.BenchControl.Sequences goto stop; } - completed = (modelessDlg as GenericDevices.IHasCompleted).Completed; + /// Update 'bool completed' + completed = !(modelessDlg is GenericDevices.IHasCompleted) || (modelessDlg as GenericDevices.IHasCompleted).Completed; + if (lastDataEntryCmpnt != null) completed = completed && lastDataEntryCmpnt.Completed; } while (!completed); } @@ -617,6 +621,7 @@ namespace TBF.BenchControl.Sequences /// /// Read start info from the DB @ cycle start /// + if (readingStartInfo.Operations.Count > 0) { readingStartInfo.AddOperation(checkUiOp).EnterState(); @@ -653,7 +658,12 @@ namespace TBF.BenchControl.Sequences //---------------------------------------------------------- selection = MakeSelection(MKSelContext.InsideProcedure); - switch (selection) + if (selection == Selection.Shutdown) + { + Shutdown(); + return; + } + switch (selection) { case Selection.Q1: selectedTestName = "Q1"; break; case Selection.Q2: selectedTestName = "Q2"; break; @@ -1403,7 +1413,7 @@ namespace TBF.BenchControl.Sequences if (draining3 && StateMachine.Tank3.IsEmpty()) break; /// Quit the selection loop and leave MakeSelection() - if (e.Contains(Event.UiCmdShutdown) && (context == MKSelContext.ProcedureNotSelected)) return Selection.Shutdown; + if (e.Contains(Event.UiCmdShutdown)) return Selection.Shutdown; if (e.Contains(Event.UiCmdBreak) && (context == MKSelContext.InsideProcedure)) return Selection.Break; if (e.Contains(Event.UiCmdPurgeBegin)) return Selection.PurgeBegin; if (e.Contains(Event.UiCmdPurgeEnd)) return Selection.PurgeEnd; diff --git a/TBF/BenchControl/Sequences/SequenceBase.cs b/TBF/BenchControl/Sequences/SequenceBase.cs index 7f8f3d493..f05b03d6f 100644 --- a/TBF/BenchControl/Sequences/SequenceBase.cs +++ b/TBF/BenchControl/Sequences/SequenceBase.cs @@ -71,6 +71,9 @@ namespace TBF.BenchControl.Sequences protected IOperation enduranceDataLoggingOp; + protected GenericDevices.IDataEntry lastDataEntryCmpnt; + + /// /// Process data logging /// @@ -756,13 +759,12 @@ namespace TBF.BenchControl.Sequences protected bool OpenCycleBeginForm() { IList e; - GenericDevices.IDataEntry dataEntryCmpnt = - TbfComponents.FindComponent(StateMachine.Procedure.DataEntry) as GenericDevices.IDataEntry; - if (dataEntryCmpnt is IHasCycleBeginForm) + lastDataEntryCmpnt = TbfComponents.FindComponent(StateMachine.Procedure.DataEntry) as GenericDevices.IDataEntry; + if (lastDataEntryCmpnt is IHasCycleBeginForm) { Bridge.OnActivity(this, Strings.Enter_water_meter_data); State.Create("MainSeq : Enter begin data") - .AddPermanentOperation((dataEntryCmpnt as IHasCycleBeginForm).ShowCycleBeginFormOp()) + .AddPermanentOperation((lastDataEntryCmpnt as IHasCycleBeginForm).ShowCycleBeginFormOp()) .AddOperation(checkUiOp) .EnterState(); e = StateMachine.WaitRunDevsRunOps(); diff --git a/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlCommunicationParams.cs b/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlCommunicationParams.cs index a178bb91e..e1fa7a922 100644 --- a/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlCommunicationParams.cs +++ b/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlCommunicationParams.cs @@ -68,7 +68,7 @@ namespace TBF.BenchControl.TestMethods.iPerlCommunication retVal.Add(iPerlCommunicationForm.Write2HzCorrectionStr); retVal.Add(string.Format("{0} if enabled", iPerlCommunicationForm.ReadConfigurationStr)); retVal.Add(string.Format("{0} 80", iPerlCommunicationForm.SetTestModeStr)); - retVal.Add("iPerl_check pruefindex q2factors direction"); + retVal.Add("iPerl_check pruefindex q2factors direction prevWorkStep"); } else { diff --git a/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlCommunicationSeq.cs b/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlCommunicationSeq.cs index 2445afb3d..de143a112 100644 --- a/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlCommunicationSeq.cs +++ b/TBF/BenchControl/TestMethods/iPerlCommunication/iPerlCommunicationSeq.cs @@ -134,7 +134,7 @@ namespace TBF.BenchControl.TestMethods.iPerlCommunication { anyMeterError = true; anyError = true; - message += string.Format("Príliš veľa opakovaní testu vodomera {0}{1}", wm.WMPosition, Environment.NewLine); + message += string.Format("iPerl{0} : Príliš veľa opakovaní testu vodomera{1}", wm.WMPosition, Environment.NewLine); } } #endif @@ -158,6 +158,16 @@ namespace TBF.BenchControl.TestMethods.iPerlCommunication // message += string.Format("Príliš veľa opakovaní testu vodomera {0}{1}", wm.WMPosition, Environment.NewLine); //} } + + if (arg.ToLower() == "prevworkstep") + { + if (!wm.IsLastRecordOk) + { + anyMeterError = true; + anyError = true; + message += string.Format("iPerl{0} : Predchádzajúci krok nebol zaznamenaný{1}", wm.WMPosition, Environment.NewLine); + } + } } mtr.TestDone = true; diff --git a/TracingDB/DB.cs b/TracingDB/DB.cs index 7d92cd135..6d7214e04 100644 --- a/TracingDB/DB.cs +++ b/TracingDB/DB.cs @@ -125,105 +125,186 @@ namespace TracingDB } - public static IList FindWMParts(ISession session, string pcbNumber) - { - IList foundParts = new List(); /// initially empty + /// + /// Save a reference record and related records into the database. + /// + /// DB session + /// Referece record + public static void SaveRecords(ISession session, ReferenceRecord refRecord) + { + log.Info("Saving data to MySQL database"); - if (string.IsNullOrEmpty(pcbNumber)) return foundParts; /// return an empty list + session.SaveOrUpdate(refRecord); + log.InfoFormat(" ReferenceRecord : {0}", refRecord); + + foreach (var r in refRecord.Records) + { + session.SaveOrUpdate(r); + log.InfoFormat(" Record : {0}", r); + } + } + + + /// + /// Delete a reference record and related records from the database. + /// + /// DB session + /// Reference record + public static void DeleteRecords(ISession session, ReferenceRecord refRecord) + { + log.Info("Deleting data from MySQL database"); + + foreach (var r in refRecord.Records) + { + session.Delete(r); + log.InfoFormat(" Record : {0}", r); + } + + session.Delete(refRecord); + log.InfoFormat(" ReferenceRecord : {0}", refRecord); + } + + + /// + /// Get process of the last record with the reference part code equal to 'pcbNumber' + /// + /// DB session + /// PCB number (code) + /// Process + public static Process GetProcess(ISession session, string pcbNumber) + { + if (string.IsNullOrEmpty(pcbNumber)) return null; try { - /// Get all already existing reference records with Code == pcbNumber, referenceRecords[0] will be the most recent one - var referenceRecords = session.QueryOver() + /// Read all already existing reference records with Code==pcbNumber, referenceRecords[0] will be the most recent one + var referenceRecords = session.QueryOver() .Where(rr => (rr.Code == pcbNumber)) .OrderBy(rr => rr.TimeStamp).Desc .List(); - if (referenceRecords.Count == 0) return foundParts; /// Returns an empty list if no such ref. record found + if (referenceRecords.Count == 0) return null; /// Returns an empty list if no such ref. record found - foundParts.Add(new WMPart(referenceRecords[0])); - Entities.Process process = referenceRecords[0].Process; - /// - foreach (var refR in referenceRecords) - { - if (process != refR.Process) - { - continue; /// skip records obtained by another process - } - - IList relatedRecords = session - .QueryOver() - .Where(x => (x.ReferenceRecord == refR)) - .List(); - - - foreach (var relR in relatedRecords) - { - foundParts.Add(new WMPart(relR)); - foreach (var ws in process.Worksteps) - { - if (ws.ReferencePart == relR.Part) - { - FindWMPartsRecursively(process, ws.ReferencePart, relR.Code, session, ref foundParts); - } - } - } - } + return referenceRecords[0].Process; } catch { - } - - return foundParts; - } - - - static void FindWMPartsRecursively(Entities.Process process, Entities.Part refPart, string code, ISession session, ref IList foundParts) - { - IList referenceRecords = session - .QueryOver() - .Where(x => (x.Workstep.ReferencePart == refPart)) - .Where(x => (x.Code == code)) - .OrderBy(x => x.TimeStamp).Desc - .List(); - - if (referenceRecords.Count == 0) return; - - foreach (var refR in referenceRecords) - { - IList relatedRecords = session - .QueryOver() - .Where(x => (x.ReferenceRecord == refR)) - .List(); - - - foreach (var relR in relatedRecords) - { - foundParts.Add(new WMPart(relR)); - - foreach (var ws in process.Worksteps) - { - if (ws.ReferencePart == relR.Part) - { - FindWMPartsRecursively(process, ws.ReferencePart, relR.Code, session, ref foundParts); - } - } - } + return null; } } + + /// + /// Finds a workstep and a part to be verified (typically a previous workstep and one of its parts). + /// Updates this.verifiedWorkstep, this.verifiedPart and this.verifyReferencePart. + /// Returns false if there is no workstep or part to be verified. + /// + /// MySQL DB session + /// Process to be analyzed + /// Workstep to be analyzed + /// true if there is a verified workstep and part + public static bool AnalyzeProcess(ISession dbSession, Process process, Workstep workstep, out Workstep verifiedWorkstep, out bool verifyReferencePart, out Part verifiedPart) + { + verifiedWorkstep = null; /// Disable any verification + verifyReferencePart = true; + verifiedPart = null; + + if (process == null || workstep == null) return false; + + try + { + if (workstep.ReferencePart != null) + { + IList worksteps = dbSession.QueryOver() + .Where(x => (x.Process == process)) + .Where(x => (x.WorkstepNr < workstep.WorkstepNr)) + .OrderBy(x => x.WorkstepNr).Asc + .List(); + + for (int i = worksteps.Count - 1; i >= 0; i--) + { + if (worksteps[i].ReferencePart != null) + { + if (worksteps[i].ReferencePart.Id == workstep.ReferencePart.Id) + { + /// Enable checking reference part + verifiedWorkstep = worksteps[i]; + verifiedPart = workstep.ReferencePart; + verifyReferencePart = true; + return true; + } + + foreach (var thisStepPart in workstep.Parts) + { + if (worksteps[i].ReferencePart.Id == thisStepPart.Id) + { + verifiedWorkstep = worksteps[i]; + verifiedPart = worksteps[i].ReferencePart; + verifyReferencePart = true; + return true; + } + } + } + + foreach (var part in worksteps[i].Parts) + { + bool isUnique = (part.CodeLocation == CodeLocation.OnPart) && ((part.CodeType == CodeType.UniqueNr) + || (part.CodeType == CodeType.FlowtubeNr) + || (part.CodeType == CodeType.FlowtubeNrLU)); + + if (isUnique) + { + if (part.Id == workstep.ReferencePart.Id) + { + /// Enable checking reference part + verifiedWorkstep = worksteps[i]; + verifiedPart = part; + verifyReferencePart = false; + log.ErrorFormat("AnalyzeProcess(session , {0}, {1}) returned 'true' : verWorkstep = {2}, verPart = {3}, verRefPart = {4}", + process.Name, workstep.Name); + return true; + } + + foreach (var thisStepPart in workstep.Parts) + { + if (part.Id == thisStepPart.Id) + { + verifiedWorkstep = worksteps[i]; + verifiedPart = part; + verifyReferencePart = false; + return true; + } + } + } + } + } + } + + log.ErrorFormat("AnalyzeProcess(session , {0}, {1}) returned 'false'", process.Name, workstep.Name); + return false; + } + catch (Exception exc) + { + log.ErrorFormat("AnalyzeProcess(session , {0}, {1}) failed : {2}", process.Name, workstep.Name, exc.Message); + return false; + } + } + + /// + /// Obsolete, use WorkplaceRegistration class instead + /// public static bool RegisterWorkplace(ISession session, string workplace, string user, string ipAddress, string processName, string workstepName, DateTime valiUntil) { try { - IList wpRegs = session - .QueryOver() + IList wpRegs = session + .QueryOver() .Where(x => (x.Workplace == workplace)) .List(); if (wpRegs.Count == 0) { - Entities.WorkplaceRegistration wpReg = new Entities.WorkplaceRegistration(workplace, user, ipAddress, processName, workstepName); + WorkplaceRegistration wpReg = new WorkplaceRegistration(workplace, user, ipAddress, processName, workstepName); wpRegs.Add(wpReg); session.SaveOrUpdate(wpReg); log.InfoFormat("RegisterWorkplace(., {0}, {1}, ...) successful (new)", workplace, user); @@ -231,11 +312,11 @@ namespace TracingDB } else if (wpRegs.Count == 1) { - Entities.WorkplaceRegistration wpReg = wpRegs[0]; + WorkplaceRegistration wpReg = wpRegs[0]; wpReg.Active = true; wpReg.TimeStamp = DateTime.Now; - wpReg.ValidUntil = DateTime.Now + new TimeSpan(8, 0, 0); + wpReg.ValidUntil = valiUntil; wpReg.UserName = user; wpReg.IPAddress = ipAddress; wpReg.ProcessName = processName; @@ -258,18 +339,21 @@ namespace TracingDB } } + /// + /// Obsolete, use WorkplaceRegistration class instead + /// public static bool UnregisterWorkplace(ISession session, string workplace) { try { - IList wpRegs = session - .QueryOver() + IList wpRegs = session + .QueryOver() .Where(x => (x.Workplace == workplace)) .List(); if (wpRegs.Count == 1) { - Entities.WorkplaceRegistration wpReg = wpRegs[0]; + WorkplaceRegistration wpReg = wpRegs[0]; wpReg.Active = false; wpReg.TimeStamp = DateTime.Now;