diff --git a/Results/DB.cs b/Results/DB.cs index 1bc7a5d0f..23f06a0a2 100644 --- a/Results/DB.cs +++ b/Results/DB.cs @@ -11,6 +11,7 @@ using NHibernate.Cfg; using NHibernate.Tool.hbm2ddl; using Common; using Results.Entities; +using Results.Entities.helpers; namespace Results { @@ -107,7 +108,11 @@ namespace Results throw new Exception("Connection string was not specified"); } - if (SessionFactory == null) SessionFactory = CreateSessionFactory(); + if (SessionFactory == null) + { + DatabaseMigrationHelper.EnsureSchema(dbType, connectionString); + SessionFactory = CreateSessionFactory(); + } return SessionFactory.OpenSession(); } diff --git a/Results/Entities/MeterTestRslt.cs b/Results/Entities/MeterTestRslt.cs index beb66d7c2..3ca3854a3 100644 --- a/Results/Entities/MeterTestRslt.cs +++ b/Results/Entities/MeterTestRslt.cs @@ -41,6 +41,7 @@ namespace Results.Entities public virtual int CalibFactorLNA { get; set; } public virtual int Q2CorrRL { get; set; } public virtual int Q2CorrLR { get; set; } + public virtual int? FlipMode { get; set; } /// null=not recorded, 0=Constant, 1=Randomized public virtual string ExtraDataPath { get; set; } /// Relative path to a file with opto-data/raw-data public virtual float X1 { get; set; } public virtual float X2 { get; set; } @@ -207,6 +208,7 @@ namespace Results.Entities CalibFactorLNA = src.CalibFactorLNA; Q2CorrRL = src.Q2CorrRL; Q2CorrLR = src.Q2CorrLR; + FlipMode = src.FlipMode; ExtraDataPath = src.ExtraDataPath; X1 = src.X1; X2 = src.X2; diff --git a/Results/Entities/helpers/DatabaseMigrationHelper.cs b/Results/Entities/helpers/DatabaseMigrationHelper.cs new file mode 100644 index 000000000..6481ed596 --- /dev/null +++ b/Results/Entities/helpers/DatabaseMigrationHelper.cs @@ -0,0 +1,144 @@ +using System; +using MySql.Data.MySqlClient; +using Common; + +namespace Results.Entities.helpers +{ + /// + /// Applies small, explicit and backwards-compatible results DB migrations + /// before NHibernate creates its first session factory. + /// + public static class DatabaseMigrationHelper + { + public static void EnsureSchema(DBType dbType, string connectionString) + { + switch (dbType) + { + case DBType.MySql: + EnsureMySqlSchema(connectionString); + break; + + case DBType.SQLite: + EnsureSQLiteSchema(connectionString); + break; + } + } + + private static void EnsureMySqlSchema(string connectionString) + { + using (var conn = new MySqlConnection(connectionString)) + { + conn.Open(); + + // Q3Channel migrations from develop/SLM-PT50_genesisDirectDecode_special_2. + // Kept as a visible template only; the related Q3 logic is not part of this change. + // EnsureColumnMySql(conn, "WaterMeterData", "Q3Channel", "INT NOT NULL DEFAULT 0"); + // EnsureColumnMySql(conn, "WaterMeter", "Q3Channel", "INT NOT NULL DEFAULT 0"); + // EnsureColumnMySql(conn, "MeterTestRslt", "Q3Channel", "INT NOT NULL DEFAULT 0"); + + EnsureColumnMySql(conn, "MeterTestRslt", "FlipMode", "INT NULL"); + } + } + + private static void EnsureColumnMySql( + MySqlConnection conn, + string tableName, + string columnName, + string columnDefinition) + { + using (var transaction = conn.BeginTransaction()) + { + try + { + bool exists; + + using (var cmd = conn.CreateCommand()) + { + cmd.Transaction = transaction; + cmd.CommandText = @" + SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = @tableName + AND COLUMN_NAME = @columnName"; + + cmd.Parameters.AddWithValue("@tableName", tableName); + cmd.Parameters.AddWithValue("@columnName", columnName); + exists = Convert.ToInt32(cmd.ExecuteScalar()) > 0; + } + + if (!exists) + { + using (var alter = conn.CreateCommand()) + { + alter.Transaction = transaction; + alter.CommandText = "ALTER TABLE `" + tableName + "` ADD COLUMN `" + + columnName + "` " + columnDefinition; + alter.ExecuteNonQuery(); + } + } + + transaction.Commit(); + } + catch + { + transaction.Rollback(); + throw; + } + } + } + + private static void EnsureSQLiteSchema(string databaseFile) + { + using (var conn = new System.Data.SQLite.SQLiteConnection("Data Source=" + databaseFile)) + { + conn.Open(); + + // Q3Channel migrations from develop/SLM-PT50_genesisDirectDecode_special_2. + // Kept as a visible template only; the related Q3 logic is not part of this change. + // EnsureColumnSQLite(conn, "WaterMeterData", "Q3Channel", "INTEGER NOT NULL DEFAULT 0"); + // EnsureColumnSQLite(conn, "WaterMeter", "Q3Channel", "INTEGER NOT NULL DEFAULT 0"); + // EnsureColumnSQLite(conn, "MeterTestRslt", "Q3Channel", "INTEGER NOT NULL DEFAULT 0"); + + EnsureColumnSQLite(conn, "MeterTestRslt", "FlipMode", "INTEGER NULL"); + } + } + + private static void EnsureColumnSQLite( + System.Data.SQLite.SQLiteConnection conn, + string tableName, + string columnName, + string columnDefinition) + { + bool exists = false; + + using (var cmd = conn.CreateCommand()) + { + cmd.CommandText = "PRAGMA table_info(" + tableName + ")"; + + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + if (string.Equals(reader["name"].ToString(), columnName, + StringComparison.OrdinalIgnoreCase)) + { + exists = true; + break; + } + } + } + } + + if (!exists) + { + using (var alter = conn.CreateCommand()) + { + alter.CommandText = "ALTER TABLE " + tableName + " ADD COLUMN " + + columnName + " " + columnDefinition; + alter.ExecuteNonQuery(); + } + } + } + } +} diff --git a/Results/ItemID.cs b/Results/ItemID.cs index 131e957db..62840ffad 100644 --- a/Results/ItemID.cs +++ b/Results/ItemID.cs @@ -361,6 +361,7 @@ namespace Results Unit_per_pulse_Read, /// 305 Pulses_per_unit, /// 306 + FlipMode, /// 307 iPerl mode used for this meter test result Count, } } diff --git a/Results/Mappings/MeterTestRsltMap.cs b/Results/Mappings/MeterTestRsltMap.cs index 9789b4608..603babf17 100644 --- a/Results/Mappings/MeterTestRsltMap.cs +++ b/Results/Mappings/MeterTestRsltMap.cs @@ -33,6 +33,7 @@ namespace Results.Mappings Map(x => x.PassedMass); Map(x => x.QuantityUnits); #if IPERL + Map(x => x.FlipMode); Map(x => x.ExtraDataPath); Map(x => x.X1); Map(x => x.X2); diff --git a/Results/Results.csproj b/Results/Results.csproj index c19b74969..b03ba382b 100644 --- a/Results/Results.csproj +++ b/Results/Results.csproj @@ -53,6 +53,9 @@ ..\packages\NHibernate.4.0.4.4000\lib\net40\NHibernate.dll + + ..\packages\System.Data.SQLite.2.0.3\lib\net471\System.Data.SQLite.dll + @@ -69,6 +72,7 @@ + diff --git a/Results/WMeterRsltItemSpec.cs b/Results/WMeterRsltItemSpec.cs index df0c1893c..9c3e3f0d7 100644 --- a/Results/WMeterRsltItemSpec.cs +++ b/Results/WMeterRsltItemSpec.cs @@ -136,6 +136,26 @@ namespace Results /// AllItems.Add(new WMeterRsltItemSpec(ItemID.RR_Quantity, "RR Quantity", "", Quantity.RegisterReader, ItemCategory.Other, (w,t,u,f,p) => (w.GetMeterTestRslt(t) != null) ? FormatStr(f, w.GetMeterTestRslt(t).GetQuantityFromUnits()) : string.Empty)); AllItems.Add(new WMeterRsltItemSpec(ItemID.RR_Units, "RR Units", "", Quantity.RegisterReader, ItemCategory.Other, (w,t,u,f,p) => (w.GetMeterTestRslt(t) != null) ? FormatStr(f, w.GetMeterTestRslt(t).QuantityUnits) : string.Empty)); +#if IPERL + AllItems.Add(new WMeterRsltItemSpec( ItemID.FlipMode, "iPerl Flip mode ()", "Flip mode used by this water meter during the selected test", + Quantity.RegisterReader, + ItemCategory.MeterResult, + (w,t,u,f,p) => + { + MeterTestRslt meterResult = w.GetMeterTestRslt(t); + if (meterResult == null || !meterResult.FlipMode.HasValue) + return string.Empty; + + string value; + switch (meterResult.FlipMode.Value) + { + case 0: value = "Constant"; break; + case 1: value = "Randomized"; break; + default: value = string.Format("Unknown ({0})", meterResult.FlipMode.Value); break; + } + return FormatStr(f, value); + })); +#endif /// Common AllItems.Add(new WMeterRsltItemSpec(ItemID.Test_name, Strings.Test + "()", Quantity.String, ItemCategory.TestData, (w,t,u,f,p) => FormatStr(f, (w.GetTestRslt(t) == null) ? "" : w.GetTestRslt(t).Name()))); diff --git a/TBF/Rig/TestMethods/DiverterTest/DiverterTestSeq.cs b/TBF/Rig/TestMethods/DiverterTest/DiverterTestSeq.cs index 0ef808421..0205c7c41 100644 --- a/TBF/Rig/TestMethods/DiverterTest/DiverterTestSeq.cs +++ b/TBF/Rig/TestMethods/DiverterTest/DiverterTestSeq.cs @@ -731,6 +731,9 @@ namespace TBF.Rig.TestMethods.DiverterTest meterRslt.WaterMeter.ResultCode |= iPerl.ResultCode; } #if IPERL + meterRslt.FlipMode = iPerl.ConfigStruct != null && iPerl.ConfigStruct.FlipMode.HasValue + ? (int?)iPerl.ConfigStruct.FlipMode.Value + : null; meterRslt.WaterMeter.CalibFactor = iPerl.CalibFactor; #endif iPerl.LastTestResult2 = iPerl.LastTestResult; /// Save shift previous test result diff --git a/TBF/Rig/TestMethods/Endurance/EnduranceSeq.cs b/TBF/Rig/TestMethods/Endurance/EnduranceSeq.cs index 5369ddfd7..5ecf91c35 100644 --- a/TBF/Rig/TestMethods/Endurance/EnduranceSeq.cs +++ b/TBF/Rig/TestMethods/Endurance/EnduranceSeq.cs @@ -461,6 +461,11 @@ namespace TBF.Rig.TestMethods.Endurance { meterRslt.WaterMeter.ResultCode |= iPerl.ResultCode; } +#if IPERL + meterRslt.FlipMode = iPerl.ConfigStruct != null && iPerl.ConfigStruct.FlipMode.HasValue + ? (int?)iPerl.ConfigStruct.FlipMode.Value + : null; +#endif iPerl.LastTestResult2 = iPerl.LastTestResult; /// Save shift previous test result iPerl.LastTestResult = meterRslt; /// Save this test result diff --git a/TBF/Rig/TestMethods/FlyingStart/FlyingStartSeq.cs b/TBF/Rig/TestMethods/FlyingStart/FlyingStartSeq.cs index 8b81c2ce5..b4e75deb3 100644 --- a/TBF/Rig/TestMethods/FlyingStart/FlyingStartSeq.cs +++ b/TBF/Rig/TestMethods/FlyingStart/FlyingStartSeq.cs @@ -737,6 +737,9 @@ namespace TBF.Rig.TestMethods.FlyingStart } #if IPERL meterRslt.WaterMeter.CalibFactor = iPerl.CalibFactor; + meterRslt.FlipMode = iPerl.ConfigStruct != null && iPerl.ConfigStruct.FlipMode.HasValue + ? (int?)iPerl.ConfigStruct.FlipMode.Value + : null; meterRslt.ExtraDataPath = iPerl.ExtraDataPath; if (TestMethods.iPerlCommunication.iPerlHead.IperlHead.FeatureVectorSize >= 1) meterRslt.X1 = iPerl.X[0]; diff --git a/TBF/Rig/TestMethods/FlyingStartFirstRepetWithMassColl/FlyingStartFirstRepetWithMassCollSeq.cs b/TBF/Rig/TestMethods/FlyingStartFirstRepetWithMassColl/FlyingStartFirstRepetWithMassCollSeq.cs index e1e6ecc10..2df168498 100644 --- a/TBF/Rig/TestMethods/FlyingStartFirstRepetWithMassColl/FlyingStartFirstRepetWithMassCollSeq.cs +++ b/TBF/Rig/TestMethods/FlyingStartFirstRepetWithMassColl/FlyingStartFirstRepetWithMassCollSeq.cs @@ -942,6 +942,9 @@ namespace TBF.Rig.TestMethods.FlyingStartFirstRepetWithMassColl } #if IPERL meterRslt.WaterMeter.CalibFactor = iPerl.CalibFactor; + meterRslt.FlipMode = iPerl.ConfigStruct != null && iPerl.ConfigStruct.FlipMode.HasValue + ? (int?)iPerl.ConfigStruct.FlipMode.Value + : null; #endif iPerl.LastTestResult2 = iPerl.LastTestResult; /// Save shift previous test result iPerl.LastTestResult = meterRslt; /// Save this test result diff --git a/TBF/Rig/TestMethods/FlyingStartMassCollComparative/FlyingStartMassCollComparativeSeq.cs b/TBF/Rig/TestMethods/FlyingStartMassCollComparative/FlyingStartMassCollComparativeSeq.cs index b92cdc938..bbb37c6a1 100644 --- a/TBF/Rig/TestMethods/FlyingStartMassCollComparative/FlyingStartMassCollComparativeSeq.cs +++ b/TBF/Rig/TestMethods/FlyingStartMassCollComparative/FlyingStartMassCollComparativeSeq.cs @@ -989,6 +989,9 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollComparative meterRslt.WaterMeter.ResultCode |= iPerl.ResultCode; } #if IPERL + meterRslt.FlipMode = iPerl.ConfigStruct != null && iPerl.ConfigStruct.FlipMode.HasValue + ? (int?)iPerl.ConfigStruct.FlipMode.Value + : null; meterRslt.WaterMeter.CalibFactor = iPerl.CalibFactor; #endif iPerl.LastTestResult2 = iPerl.LastTestResult; /// Save shift previous test result diff --git a/TBF/Rig/TestMethods/FlyingStartMassCollProlonged/FlyingStartMassCollProlongedSeq.cs b/TBF/Rig/TestMethods/FlyingStartMassCollProlonged/FlyingStartMassCollProlongedSeq.cs index bd7235132..c0be7ca24 100644 --- a/TBF/Rig/TestMethods/FlyingStartMassCollProlonged/FlyingStartMassCollProlongedSeq.cs +++ b/TBF/Rig/TestMethods/FlyingStartMassCollProlonged/FlyingStartMassCollProlongedSeq.cs @@ -874,6 +874,9 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollProlonged } #if IPERL meterRslt.WaterMeter.CalibFactor = iPerl.CalibFactor; + meterRslt.FlipMode = iPerl.ConfigStruct != null && iPerl.ConfigStruct.FlipMode.HasValue + ? (int?)iPerl.ConfigStruct.FlipMode.Value + : null; meterRslt.ExtraDataPath = iPerl.ExtraDataPath; if (TestMethods.iPerlCommunication.iPerlHead.IperlHead.FeatureVectorSize >= 1) meterRslt.X1 = iPerl.X[0]; if (TestMethods.iPerlCommunication.iPerlHead.IperlHead.FeatureVectorSize >= 2) meterRslt.X2 = iPerl.X[1]; diff --git a/TBF/Rig/TestMethods/FlyingStartMassCollection/FlyingStartMassCollectionSeq.cs b/TBF/Rig/TestMethods/FlyingStartMassCollection/FlyingStartMassCollectionSeq.cs index a5e6054df..6655064f3 100644 --- a/TBF/Rig/TestMethods/FlyingStartMassCollection/FlyingStartMassCollectionSeq.cs +++ b/TBF/Rig/TestMethods/FlyingStartMassCollection/FlyingStartMassCollectionSeq.cs @@ -1179,6 +1179,9 @@ namespace TBF.Rig.TestMethods.FlyingStartMassCollection } #if IPERL meterRslt.WaterMeter.CalibFactor = iPerl.CalibFactor; + meterRslt.FlipMode = iPerl.ConfigStruct != null && iPerl.ConfigStruct.FlipMode.HasValue + ? (int?)iPerl.ConfigStruct.FlipMode.Value + : null; meterRslt.ExtraDataPath = iPerl.ExtraDataPath; if (TestMethods.iPerlCommunication.iPerlHead.IperlHead.FeatureVectorSize >= 1) meterRslt.X1 = iPerl.X[0]; if (TestMethods.iPerlCommunication.iPerlHead.IperlHead.FeatureVectorSize >= 2) meterRslt.X2 = iPerl.X[1]; diff --git a/TBF/Rig/TestMethods/FlyingStartTankCollection/FlyingStartTankCollectionSeq.cs b/TBF/Rig/TestMethods/FlyingStartTankCollection/FlyingStartTankCollectionSeq.cs index 01e9ed488..80ef616e1 100644 --- a/TBF/Rig/TestMethods/FlyingStartTankCollection/FlyingStartTankCollectionSeq.cs +++ b/TBF/Rig/TestMethods/FlyingStartTankCollection/FlyingStartTankCollectionSeq.cs @@ -633,6 +633,9 @@ namespace TBF.Rig.TestMethods.FlyingStartTankCollection meterRslt.WaterMeter.ResultCode |= iPerl.ResultCode; } #if IPERL + meterRslt.FlipMode = iPerl.ConfigStruct != null && iPerl.ConfigStruct.FlipMode.HasValue + ? (int?)iPerl.ConfigStruct.FlipMode.Value + : null; meterRslt.WaterMeter.CalibFactor = iPerl.CalibFactor; #endif iPerl.LastTestResult2 = iPerl.LastTestResult; /// Save shift previous test result diff --git a/TBF/Rig/TestMethods/iPerlCommunication/iPerlCommunicationForm.cs b/TBF/Rig/TestMethods/iPerlCommunication/iPerlCommunicationForm.cs index 05223a3c1..94dc7e846 100644 --- a/TBF/Rig/TestMethods/iPerlCommunication/iPerlCommunicationForm.cs +++ b/TBF/Rig/TestMethods/iPerlCommunication/iPerlCommunicationForm.cs @@ -1214,12 +1214,6 @@ namespace TBF.Rig.TestMethods.iPerlCommunication config.ReadingUnits.Value.ToString(); } - if (config.FlipMode.HasValue) - { - wm.RadioAddress = - config.FlipMode.Value.ToString(); - } - if (config.Calibration != null) { wm.CalibFactor = @@ -1231,7 +1225,7 @@ namespace TBF.Rig.TestMethods.iPerlCommunication "FWVersion={0}, ReadingUnits={1}, FlipMode={2}, CalibFactor={3}", wm.FWVersion, wm.SerialNrAux, - wm.RadioAddress, + config.FlipMode.HasValue ? config.FlipMode.Value.ToString() : "", wm.CalibFactor); } else