/// <summary> /// writes the text to the specified file /// </summary> /// <param name="text">the text to write</param> /// <param name="path">the path of the file</param> /// <param name="append">if true, writes the text to the end of the file, otherwise overrides any existing file</param> /// <returns></returns> public async Task WriteTextToFileAsync(string text, string path, bool append = false) { // TODO: Add exception catching // normalize and resolve path path = NormalizePath(path); // resolve to absolute path path = ResolvePath(path); // is the path exits? if (!Directory.Exists(Path.GetDirectoryName(path))) { // generate a new folder in specified location Directory.CreateDirectory(Path.GetDirectoryName(path)); } // lock the task await AsyncAwaiter.AwaitAsync(nameof(FileManager) + path, async() => { // run the synchronous file access as new task await IoC.Task.Run(() => { // write the log message to a file using (var fileStream = (TextWriter) new StreamWriter(File.Open(path, append ? FileMode.Append : FileMode.Create))) fileStream.Write(text); }); }); }
/// <summary> /// Handles Omicron Hardware Configuration Settings /// </summary> /// <returns>Returns new Hardware Configuration</returns> public async Task HardwareConfiguration() { // is test running? if (!IoC.CMCControl.IsTestRunning) { // there is a test set attached so run specified tests. // lock the task await AsyncAwaiter.AwaitAsync(nameof(HardwareConfiguration), async() => { // set visibility of "Hardware Configuration" animation IoC.Commands.IsConfigurationAvailable = true; // find cmc if (await IoC.Task.Run(() => IoC.FindCMC.Find())) { // let log start await IoC.Task.Run(() => IoC.Logger.Log($"{nameof(HardwareConfiguration)} started.")); // update device info await IoC.Task.Run(() => IoC.Logger.Log($"Following device associated: {IoC.CMCControl.DeviceInfo}.")); // save current page so we can return to it. OldApplicationPage = IoC.Application.CurrentPage; // save current view model so we can return to it. OldViewModel = IoC.Application.CurrentPageViewModel; // update log file about the connected Omicron capabilities. await IoC.Task.Run(() => IoC.Logger.Log($"Following hardware configurations available:")); // retrieve voltage capabilities if the list is empty. IoC.Settings.OmicronVoltageOutputs = await IoC.Configurations.Get("voltage"); // retrieve current capabilities. IoC.Settings.OmicronCurrentOutputs = await IoC.Configurations.Get("current"); // set visibility of command buttons IoC.Commands.Cancellation = true; IoC.Commands.LoadTestAvailable = false; IoC.Commands.StartTestAvailable = false; IoC.Commands.NewTestAvailable = IoC.TestDetails.SelectedCurrentConfiguration.CurrentWiringDiagram || IoC.TestDetails.SelectedVoltageConfiguration.CurrentWiringDiagram; IoC.Commands.ConfigurationAvailable = false; // change color of the Add New Test button to green. IoC.Commands.CancelForegroundColor = "00ff00"; // Show Settings page IoC.Application.GoToPage(ApplicationPage.Settings, IoC.Settings); // disconnect from attached Omicron Test Set await IoC.ReleaseOmicron.ReleaseAsync(); } }); } }
/// <summary> /// Handles errors and stops the app gracefully. /// </summary> /// <param name="userRequest">true if test interrupt requested by the user /// false if test completed itself</param> public async Task ProcessErrorsAsync(bool userRequest = true) { await AsyncAwaiter.AwaitAsync(nameof(ProcessErrorsAsync), async() => { try { // update developer IoC.Logger.Log($"Test {(userRequest ? "interrupted" : "completed")}", LogLevel.Informative); // update the user IoC.Communication.Log = $"{DateTime.Now.ToLocalTime():MM/dd/yy hh:mm:ss.fff}: Test { (userRequest ? "interrupted by the user." : "completed.")}"; // Turn off outputs of Omicron Test Set. await IoC.Task.Run(async() => await IoC.PowerOptions.TurnOffCMCAsync()); // if the user wants to stop the test if (userRequest) { // update In Progress test file IoC.Communication.UpdateCurrentTestFileListItem(CommunicationViewModel.TestStatus.Interrupted); } // Disconnect Modbus Communication. await IoC.Task.Run(() => IoC.Communication.EAModbusClient.Disconnect()).ConfigureAwait(continueOnCapturedContext: false);; // check if timer is initialized then dispose it. await IoC.Task.Run(() => IoC.CMCControl.MdbusTimer?.Dispose()).ConfigureAwait(continueOnCapturedContext: false); // Progress bar is invisible IoC.CMCControl.IsTestRunning = IoC.Commands.IsConnectionCompleted = IoC.Commands.IsConnecting = IoC.Communication.EAModbusClient.Connected; // change color of Cancel Command button to Red IoC.Commands.CancelForegroundColor = "ff0000"; // release omicron test set. await IoC.Task.Run(async() => await ReleaseAsync()); } catch (Exception) { // re-throw error throw; } });
/// <summary> /// connects to omicron and test unit. /// </summary> private async Task ConnectOmicronAndUnitAsync() { // there is a test set attached so run specified tests. // lock the task await AsyncAwaiter.AwaitAsync(nameof(ConnectOmicronAndUnitAsync), async() => { // decides which signal is our ramping signal by comparing the mismatch of any "From" and "To" values. // after the user clicked "Go" button TestSignal testSignal = new TestSignal(); // is any signal ramping? if (testSignal.IsRamping) { // is the user selected a hardware configuration? if (testSignal.IsRunningPermitted) { // define the cancellation token source. TokenSource = new CancellationTokenSource(); // define the cancellation token to use // terminate tests prematurely. Token = TokenSource.Token; // Run test command await IoC.Task.Run(() => IoC.TestDetails.ConnectCommand.Execute(IoC.TestDetails), Token); } else { // inform the user there is no hardware configuration available IoC.Communication.Log = $"{DateTime.Now.ToLocalTime():MM/dd/yy HH:mm:ss.fff}: Running test is not permitted due to hardware configuration. Please check your configuration."; } } else { // inform the user there is no test case IoC.Communication.Log = $"{DateTime.Now.ToLocalTime():MM/dd/yy HH:mm:ss.fff}: There is no ramping signal. Please check your entries."; } });
/// <summary> /// Turns on outputs of Omicron Test Set. /// </summary> public async Task TurnOnCMCAsync() { try { // lock the task await AsyncAwaiter.AwaitAsync(nameof(TurnOnCMCAsync), async() => { // update the developer IoC.Logger.Log($"{nameof(TurnOnCMCAsync)} started.", LogLevel.Informative); // Send command to Turn On Analog Outputs await IoC.Task.Run(() => IoC.StringCommands.SendStringCommandsAsync(OmicronStringCmd.out_ana_on)); // update the developer IoC.Logger.Log($"{nameof(TurnOnCMCAsync)} completed.", LogLevel.Informative); }); } catch (Exception) { // re-throw error throw; } }
/// <summary> /// Allows to read test register values separated with comma specified by "Measurement Interval". /// </summary> public void MeasurementIntervalCallback(object Register) { // generate register(s) list List <string> registerStrings = new List <string>(); // add Register to the list registerStrings.AddRange(Register.ToString().Split(',')); // Use ParallelOptions instance to store the CancellationToken ParallelOptions parallelingOptions = new ParallelOptions { // associate cancellation token. CancellationToken = IoC.Commands.Token, // set limit for the parallelism equivalent of hardware core count MaxDegreeOfParallelism = Environment.ProcessorCount }; // Partition the entire source array. var rangePartitioner = Partitioner.Create(0, registerStrings.Count); // inquire each register that specified by the user. Parallel.ForEach(rangePartitioner, parallelingOptions, async(registerString) => { using (CancellationTokenSource cancellation = new CancellationTokenSource(new TimeSpan(0, 0, 0, Convert.ToInt32(IoC.TestDetails.MeasurementInterval)))) { // Listening to the cancellation event either the user or test completed. var cancellationTask = Task.Run(() => { if (IoC.Commands.Token.IsCancellationRequested) { // Sending the cancellation message cancellation.Cancel(); } }); try { // Loop over each range element without a delegate invocation. for (int i = registerString.Item1; i < registerString.Item2; i++) { // start a task to read register address specified by the user. await IoC.Task.Run(async() => { // is the register is between modbus holding register range? if (ushort.TryParse(registerStrings[i].Trim(), out ushort register)) { // lock the task await AsyncAwaiter.AwaitAsync(nameof(MeasurementIntervalCallback), async() => { // start a task to read holding register (Function 0x03) int[] serverResponse = await IoC.Task.Run(() => ReadHoldingRegisterWithCancellationAsync(register: register, cancellationToken: cancellation.Token)); // decide if serverResponse is acceptable only criteria is the length of the response. if (serverResponse.Length > 0) { // server response must be between 0 and 65535 if (ushort.MinValue <= Math.Abs(serverResponse[0]) && ushort.MaxValue >= Math.Abs(serverResponse[0])) { // add new reading with the current time. IoC.CMCControl.RegisterReadingsWithTime[i].Add(DateTime.Now, serverResponse[0]); } } else { // server failed to respond. Ignoring it until find a better option. // inform the developer about error IoC.Logger.Log($"register: {register} -- serverResponse : No server response"); } }); } else { // illegal register address throw new ArgumentOutOfRangeException($"Register: {registerStrings[i].Trim()} is out of range"); } }); } } catch (Exception) { // re-throw error throw; } // cancellation.Dispose(); await cancellationTask; } }); }
/// <summary> /// Starts a test with the values specified in Nominal Values page and /// Communication page. /// </summary> public async Task StartCommunicationAsync() { // start point of all test steps with the first mouse click and it will ignore subsequent mouse clicks await AsyncAwaiter.AwaitAsync(nameof(StartCommunicationAsync), async() => { using (IoC.Commands.TokenSource) { try { // update the user Log = $"{DateTime.Now.ToLocalTime():MM/dd/yy HH:mm:ss.fff}: Communication starts."; // get new construct of ModbusClient EAModbusClient = new EasyModbus.ModbusClient { IPAddress = IpAddress, Port = Convert.ToInt32(Port), ConnectionTimeout = 20000, // LogFileFilename = @"C:\Users\TBircek\Documents\metering\modbus.log" }; // Checks if the Server IPAddress is available if (EAModbusClient.Available(20000)) { // connect to the server EAModbusClient.Connect(); // find any CMCEngine attached to this computer if (IoC.FindCMC.Find()) { // Is there Omicron Test Set attached to this app? if (IoC.CMCControl.DeviceID > 0) { // indicates the test is running. IoC.CMCControl.IsTestRunning = true; try { // perform initial set up on CMCEngine await IoC.Task.Run(async() => await IoC.InitialCMCSetup.InitialSetupAsync()); // there is a test set attached so run specified tests. await IoC.Task.Run(async() => await IoC.CMCControl.TestAsync(IoC.Commands.Token)); } catch (OperationCanceledException ex) { // inform the developer about error IoC.Logger.Log($"Exception is : {ex.Message}"); // update the user about the error. IoC.Communication.Log = $"{DateTime.Now.ToLocalTime():MM/dd/yy HH:mm:ss.fff}: Exception: {ex.Message}."; } catch (Exception ex) { // inform developer IoC.Logger.Log($"Exception: {ex.Message}"); // update the user about failed test. IoC.Communication.Log = $"{DateTime.Now.ToLocalTime():MM/dd/yy HH:mm:ss.fff}: Test failed: {ex.Message}."; // catch inner exceptions if exists if (ex.InnerException != null) { // inform the user about more details about error. IoC.Communication.Log = $"{DateTime.Now.ToLocalTime():MM/dd/yy HH:mm:ss.fff}: Inner exception: {ex.InnerException}."; } } finally { // Trying to stop the app gracefully. await IoC.Task.Run(() => IoC.ReleaseOmicron.ProcessErrorsAsync(false)); } } else { // inform the user Log = $"{DateTime.Now.ToLocalTime():MM/dd/yy HH:mm:ss.fff}: Failed: Omicron Test Set ID is a zero."; } } else { // inform the user Log = $"{DateTime.Now.ToLocalTime():MM/dd/yy HH:mm:ss.fff}: Failed: There is no attached Omicron Test Set. Please attached a Omicron Test Set before test."; } } else { // inform the user Log = $"{DateTime.Now.ToLocalTime():MM/dd/yy HH:mm:ss.fff}: Failed: The server is not available: {EAModbusClient.IPAddress}."; } } catch (Exception ex) { // inform the user about error. Log = $"{DateTime.Now.ToLocalTime():MM/dd/yy HH:mm:ss.fff}: Start Communication failed: {ex.Message}."; // catch inner exceptions if exists if (ex.InnerException != null) { // inform the user about more details about error. Log = $"{DateTime.Now.ToLocalTime():MM/dd/yy HH:mm:ss.fff}: Inner exception: {ex.InnerException}."; } } } });