diff --git a/Tractor/Com.QuantAsylum.Tractor.Database/AuditDb.cs b/Tractor/Com.QuantAsylum.Tractor.Database/AuditDb.cs index ad93f20..50c9be9 100644 --- a/Tractor/Com.QuantAsylum.Tractor.Database/AuditDb.cs +++ b/Tractor/Com.QuantAsylum.Tractor.Database/AuditDb.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Net.Http; @@ -15,8 +16,8 @@ class AuditData public string ProductId; public string SerialNumber; public string SessionName; - public string Channel; - public string TestGroup; + public string Channel = ""; // Needed to accept data in old format. Without this there's a server exception + public string TestGroup = ""; // Ditto public string TestFile; public string TestFileMd5; public string Name; @@ -59,17 +60,58 @@ static public int AuditQueueDepth get { return _AuditQueueDepth; } } - static public void StartBackgroundWorker() + static public void StartBackgroundTask() { - var task = new Task(() => BackgroundWorker(), TaskCreationOptions.LongRunning); + var task = new Task(() => BackgroundTask(), TaskCreationOptions.LongRunning); task.Start(); } + static DateTime LastServerAccess = DateTime.Now; + + /// + /// Ensure server accesses happen at least 1 second apart. Accesses from same IP + /// address less than 1 second apart are ignored. + /// + /// + /// + static Task GetAsync(string requestUri) + { + while (DateTime.Now.Subtract(LastServerAccess).TotalSeconds < 1.05) + { + Thread.Sleep(100); + } + + //Debug.WriteLine($"Last Server Access: {DateTime.Now.Subtract(LastServerAccess).TotalSeconds:0.0} seconds ago"); + Task result = Client.GetAsync(requestUri); + LastServerAccess = DateTime.Now; + return result; + } + + /// + /// Ensure server accesses happen at last 1 second apart. Accesses from same IP + /// address less than 1 second apart are ignored. + /// + /// + /// + /// + static Task PostAsync(string requestUri, HttpContent body) + { + while (DateTime.Now.Subtract(LastServerAccess).TotalSeconds < 1.05) + { + Thread.Sleep(100); + } + + //Debug.WriteLine($"Last Server Access: {DateTime.Now.Subtract(LastServerAccess).TotalSeconds:0.0} seconds ago"); + Task result = Client.PostAsync(requestUri, body); + LastServerAccess = DateTime.Now; + return result; + } + static public string CheckService() { try { - var response = Client.GetAsync(Url + "/api/CheckService").Result; + var response = GetAsync(Url + "/api/CheckService").Result; if (response.IsSuccessStatusCode) { @@ -111,7 +153,7 @@ static public List QueryGroupsBySerialNumber(string pid, string sn) var json = new JavaScriptSerializer().Serialize(d); var body = new StringContent(json, Encoding.UTF8, "application/json"); - var response = Client.PostAsync(Url + "/api/QueryGroups", body).Result; + var response = PostAsync(Url + "/api/QueryGroups", body).Result; string content = response.Content.ReadAsStringAsync().Result; JavaScriptSerializer jsSerializer = new JavaScriptSerializer(); @@ -141,7 +183,7 @@ static public string QueryTestsByGroup(string pid, string group) var json = new JavaScriptSerializer().Serialize(d); var body = new StringContent(json, Encoding.UTF8, "application/json"); - var response = Client.PostAsync(Url + "/api/QueryTests", body).Result; + var response = PostAsync(Url + "/api/QueryTests", body).Result; string content = response.Content.ReadAsStringAsync().Result; JavaScriptSerializer jsSerializer = new JavaScriptSerializer(); @@ -177,7 +219,7 @@ static public List QueryTestNames(string pid) var json = new JavaScriptSerializer().Serialize(d); var body = new StringContent(json, Encoding.UTF8, "application/json"); - var response = Client.PostAsync(Url + "/api/QueryTestNames", body).Result; + var response = PostAsync(Url + "/api/QueryTestNames", body).Result; string content = response.Content.ReadAsStringAsync().Result; JavaScriptSerializer jsSerializer = new JavaScriptSerializer(); @@ -212,7 +254,7 @@ static public string QueryStatsByTest(string pid, string testName, string testSe var json = new JavaScriptSerializer().Serialize(d); var body = new StringContent(json, Encoding.UTF8, "application/json"); - var response = Client.PostAsync(Url + "/api/QueryResults", body).Result; + var response = PostAsync(Url + "/api/QueryResults", body).Result; string content = response.Content.ReadAsStringAsync().Result; JavaScriptSerializer jsSerializer = new JavaScriptSerializer(); @@ -245,7 +287,7 @@ static public string QueryTestNamesByProductId(string pid) var json = new JavaScriptSerializer().Serialize(d); var body = new StringContent(json, Encoding.UTF8, "application/json"); - var response = Client.PostAsync(Url + "/api/QueryResults", body).Result; + var response = PostAsync(Url + "/api/QueryResults", body).Result; string content = response.Content.ReadAsStringAsync().Result; JavaScriptSerializer jsSerializer = new JavaScriptSerializer(); @@ -296,7 +338,7 @@ static bool SubmitAuditDataToCloud(AuditData d) { var json = new JavaScriptSerializer().Serialize(d); var body = new StringContent(json, Encoding.UTF8, "application/json"); - var response = Client.PostAsync(Url + "/api/AddTest", body).Result; + var response = PostAsync(Url + "/api/AddTest", body).Result; Log.WriteLine(LogType.Database, string.Format("PostAsync() to cloud finished. Response: " + response.StatusCode.ToString())); if (response.IsSuccessStatusCode) @@ -305,33 +347,39 @@ static bool SubmitAuditDataToCloud(AuditData d) return false; } - static void BackgroundWorker() + static void BackgroundTask() { - const int fileLimit = 500; + Random r = new Random(); + while (true) { try { - string[] filePaths = Directory.GetFiles(Constants.AuditPath, "*.cache", SearchOption.TopDirectoryOnly); + List filePaths = Directory.GetFiles(Constants.AuditPath, "*.cache", SearchOption.TopDirectoryOnly).ToList(); + + _AuditQueueDepth = filePaths.Count; - _AuditQueueDepth = filePaths.Length; - if (filePaths.Length > fileLimit) + // Do in batches of 50. Do in random order so if we see a bad record, we're not always + // starting with that one and trying that record over and over: If that record is corrupted + // then the effort will never finish. + if (filePaths.Count > 0) { - for (int i = 0; i <= fileLimit; i++) + for (int i = 0; i < 50; i++) { - SubmitFileToServer(filePaths[i]); - Thread.Sleep(1100); + int randIndex = r.Next(0, filePaths.Count); + if (SubmitFileToServer(filePaths[randIndex])) + { + filePaths.RemoveAt(randIndex); + _AuditQueueDepth = filePaths.Count; + } + Thread.Sleep(1000); + + if (filePaths.Count == 0) + break; } } - else if (filePaths.Length >= 1) - { - SubmitFileToServer(filePaths[0]); - Thread.Sleep(1100); - } - else - { - Thread.Sleep(5000); - } + + Thread.Sleep(5000); } catch (Exception ex) { @@ -345,7 +393,7 @@ static void BackgroundWorker() /// Reads a file from the filesystem, converts it to an Audit, and pushes to cloud /// /// - static void SubmitFileToServer(string fileName) + static bool SubmitFileToServer(string fileName) { try { @@ -355,6 +403,7 @@ static void SubmitFileToServer(string fileName) { File.Delete(fileName); Log.WriteLine(LogType.Database, "AuditDb BackgroundWorker successfully submitted a file to cloud"); + return true; } else { @@ -365,6 +414,8 @@ static void SubmitFileToServer(string fileName) { Log.WriteLine(LogType.Database, "AuditDb BackgroundWorker exception: " + ex.Message); } + + return false; } } } diff --git a/Tractor/Com.QuantAsylum.Tractor.Dialogs/DlgQuery.Designer.cs b/Tractor/Com.QuantAsylum.Tractor.Dialogs/DlgQuery.Designer.cs index d2a6d36..a9e6d04 100644 --- a/Tractor/Com.QuantAsylum.Tractor.Dialogs/DlgQuery.Designer.cs +++ b/Tractor/Com.QuantAsylum.Tractor.Dialogs/DlgQuery.Designer.cs @@ -314,6 +314,7 @@ private void InitializeComponent() this.MinimizeBox = false; this.Name = "DlgQuery"; this.Text = "Audit Database Query Parameters"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.DlgQuery_FormClosing); this.Load += new System.EventHandler(this.DlgQuery_Load); this.groupBox1.ResumeLayout(false); this.groupBox1.PerformLayout(); diff --git a/Tractor/Com.QuantAsylum.Tractor.Dialogs/DlgQuery.cs b/Tractor/Com.QuantAsylum.Tractor.Dialogs/DlgQuery.cs index c87f505..30d53a4 100644 --- a/Tractor/Com.QuantAsylum.Tractor.Dialogs/DlgQuery.cs +++ b/Tractor/Com.QuantAsylum.Tractor.Dialogs/DlgQuery.cs @@ -31,6 +31,11 @@ private void DlgQuery_Load(object sender, EventArgs e) UpdateButtonState(); } + private void DlgQuery_FormClosing(object sender, FormClosingEventArgs e) + { + + } + private void UpdateButtonState() { if (TestGroups.Count == 0 || TestGroups.Count == 1) @@ -72,9 +77,6 @@ private void button2_Click(object sender, EventArgs e) { TestGroups = AuditDb.QueryGroupsBySerialNumber(textBox2.Text, textBox1.Text); - // Need to limit rate webservice rate to no more than one call per second - Thread.Sleep(1000); - UpdateButtonState(); if (TestGroups.Count > 0) { @@ -222,5 +224,7 @@ private void LoadGuid(TextBox tb) Log.WriteLine(LogType.Error, "An exception occured load a PID fron a file: " + ex.Message); } } + + } } diff --git a/Tractor/Com.QuantAsylum.Tractor.Dialogs/DlgSettings.cs b/Tractor/Com.QuantAsylum.Tractor.Dialogs/DlgSettings.cs index c0806c4..288dcba 100644 --- a/Tractor/Com.QuantAsylum.Tractor.Dialogs/DlgSettings.cs +++ b/Tractor/Com.QuantAsylum.Tractor.Dialogs/DlgSettings.cs @@ -50,7 +50,11 @@ private void DlgSettings_Load(object sender, EventArgs e) textBox3.Text = Settings.DbSessionName; textBox4.Text = Settings.ProductId.ToString(); textBox5.Text = Settings.AuditDbSessionName; + + checkBox4.CheckedChanged -= checkBox4_CheckedChanged; checkBox4.Checked = Settings.UseAuditDb; + checkBox4.CheckedChanged += checkBox4_CheckedChanged; + textBox6.Text = Settings.AuditDbEmail; checkBox2.Checked = Settings.LockTestScreen; textBox1.Text = Settings.Password; diff --git a/Tractor/Com.QuantAsylum.Tractor.Tests/Other/MicCompareA01.cs b/Tractor/Com.QuantAsylum.Tractor.Tests/Other/MicCompareA01.cs index f3b5f08..2fffca3 100644 --- a/Tractor/Com.QuantAsylum.Tractor.Tests/Other/MicCompareA01.cs +++ b/Tractor/Com.QuantAsylum.Tractor.Tests/Other/MicCompareA01.cs @@ -33,8 +33,6 @@ public MicCompareA01() : base() { Name = this.GetType().Name; _TestType = TestTypeEnum.Other; - if (FftSize < 32768) - FftSize = 32768; } public override void DoTest(string title, out TestResult tr) @@ -59,8 +57,8 @@ public override void DoTest(string title, out TestResult tr) int passCount = 0; if (CheckPhase) { - if (((IAudioAnalyzer)Tm.TestClass).LRVerifyPhase((int)FftSize / 4)) ++passCount; - if (((IAudioAnalyzer)Tm.TestClass).LRVerifyPhase((int)FftSize / 4 + 300)) ++passCount; + if (((IAudioAnalyzer)Tm.TestClass).LRVerifyPhase((int)FftSize * 1024 / 4)) ++passCount; + if (((IAudioAnalyzer)Tm.TestClass).LRVerifyPhase((int)FftSize * 1024 / 4 + 300)) ++passCount; if (passCount != 2) passPhase = false; } @@ -88,7 +86,8 @@ public override string GetTestLimits() public override string GetTestDescription() { - return "Compares a reference microphone to a test microphone. The difference is compared to a specified mask."; + return "Compares a reference microphone (left channel) to a test microphone (right channel). The difference (L-R) is " + + "displayed and compared to a specified mask."; } internal override int HardwareMask diff --git a/Tractor/Constants.cs b/Tractor/Constants.cs index ea8967c..330aab3 100644 --- a/Tractor/Constants.cs +++ b/Tractor/Constants.cs @@ -10,7 +10,7 @@ namespace Tractor static class Constants { public static string TitleBarText = "QuantAsylum TRACTOR"; - public static readonly double Version = 0.993; + public static readonly double Version = 0.994; public static string VersionSuffix = ""; public static double RequiredWebserviceVersion = 0.5; diff --git a/Tractor/Form1.cs b/Tractor/Form1.cs index 031ca53..8c18fe6 100644 --- a/Tractor/Form1.cs +++ b/Tractor/Form1.cs @@ -113,7 +113,7 @@ private void Form1_Load(object sender, EventArgs e) SetTreeviewControls(); - Com.QuantAsylum.Tractor.Database.AuditDb.StartBackgroundWorker(); + Com.QuantAsylum.Tractor.Database.AuditDb.StartBackgroundTask(); string[] args = Environment.GetCommandLineArgs(); if (args.Length >= 2) @@ -494,7 +494,7 @@ private void newTestPlan_Click(object sender, EventArgs e) AppSettings = new AppSettings(); AppSettingsDirty = true; - UpdateTitleBar(); + SettingsFile = ""; Type t = Type.GetType(AppSettings.TestClass); Tm.TestClass = Activator.CreateInstance(t); foreach (TestBase test in AppSettings.TestList) diff --git a/Tractor/Maths.cs b/Tractor/Maths.cs index cf404d4..b70fde2 100644 --- a/Tractor/Maths.cs +++ b/Tractor/Maths.cs @@ -8,21 +8,24 @@ namespace Tractor { static class Maths { - static public void StdDev(List doubles, out double average, out double stdDev) + static public void StdDev(List data, out double avg, out double stdDev) { - double avg = 0; - average = 0; - stdDev = 0; - int count = doubles.Count(); - if (count > 1) + if (data.Count == 0) + throw new InvalidOperationException("There must be at least 1 data point in the array presented to StdDev"); + + if (data.Count == 1) { - avg = doubles.Average(); + avg = data[0]; + stdDev = 0; + return; + } - double sum = doubles.Sum(d => (d - avg) * (d - avg)); + // Can't use ref in anonymous method. Need to create mirror. + double average = data.Average(); + double sum = data.Sum(d => (d - average) * (d - average)); - stdDev = Math.Sqrt(sum / (count - 1)); - average = avg; - } + stdDev = Math.Sqrt(sum / (data.Count - 1)); + avg = average; } } } diff --git a/Tractor/Releases.txt b/Tractor/Releases.txt index f27c2ee..85dd510 100644 --- a/Tractor/Releases.txt +++ b/Tractor/Releases.txt @@ -1,4 +1,11 @@ -0.993 +0.994 +- Fixed bug where older-style audit records could get stuck and never upload to server +- Fixed bug where stuck records hogged all the upload time and fresh records were ignored +- Fixed bug so that AuditDb warning was only shown when first enabled instead of everytime you entered settings +- Updated cloud server to 0.52 +- Updates to Mic Comparison to handle new Axis and FFT + +0.993 - Fixed bug where switching tests plans could result in inability to re-establish connection to QA401. - Moved FFT sizes from bytes to kBytes. In older test-plans (pre 0.993) that are opened, they will be migrated automatically. - EfficiencyA07 wil now work on single channel or both channels