/// <summary> /// Convert the data stream values retrieved from the VCU to a format that can be plotted by the <c>FormViewDataStream</c> class. /// </summary> /// <remarks> /// The value array retrieved by from the VCU is initially mapped to the WatchIdentifierList property of Column[0] of the workset, however, the WatchElement /// array associated with each frame must be mapped to the WatchElementList property of the workset. /// </remarks> /// <param name="startTime">The start time of the fault log.</param> /// <param name="sampleCount">The number of samples in the data stream.</param> /// <param name="frameIntervalMs">The interval, in ms, between consecutive data frames.</param> /// <param name="values">The point values corresponding to each variable.</param> /// <param name="dataTypes">The data types associated with each value.</param> /// <param name="workset">The workset that is to be used to define the output format.</param> /// <returns>The watch variable values in the format that can be plotted by the <c>FormViewDataStream</c> class.</returns> /// <exception cref="ArgumentException">Thrown if one or more of the the watch identifiers defined in the WatchElementList property of the workset does nor exist.</exception> private List <WatchFrame_t> ConvertToWatchFrameList(DateTime startTime, short sampleCount, short frameIntervalMs, int[] values, short[] dataTypes, Workset_t workset) { Debug.Assert(sampleCount > 1, "CommunicationEvent.ConvertToWatchFrameList() - [pointCount > 1]"); Debug.Assert(frameIntervalMs > 0, "CommunicationEvent.ConvertToWatchFrameList() - [frameIntervalMs > 0"); Debug.Assert(values != null, "CommunicationEvent.ConvertToWatchFrameList() - [values != null]"); Debug.Assert(dataTypes != null, "CommunicationEvent.ConvertToWatchFrameList() - [dataTypes != null]"); short watchCount = (short)workset.WatchElementList.Count; // Create a look-up array to translate the watch variable data retrieved from the VCU to the order defined by the WatchElementList property of the workset. short[] translate = new short[watchCount]; short watchIdentifier, rowIndex, columnIndex; WatchVariable watchVariable; for (short watchElementIndex = 0; watchElementIndex < watchCount; watchElementIndex++) { watchIdentifier = workset.WatchElementList[watchElementIndex]; try { watchVariable = Lookup.WatchVariableTable.Items[watchIdentifier]; if (watchVariable == null) { throw new ArgumentException(Resources.MBTWatchVariableNotDefined); } } catch (Exception) { throw new ArgumentException(Resources.MBTWatchVariableNotDefined); } workset.GetWatchVariableLocation(watchVariable.OldIdentifier, out columnIndex, out rowIndex); Debug.Assert(((columnIndex != CommonConstants.NotFound) && (rowIndex != CommonConstants.NotFound)), "CommunicationEvent.ConvertToWatchFrameList() - [((columnIndex != 0) || (rowIndex != CommonConstants.NotFound))]"); translate[watchElementIndex] = rowIndex; } // Translate the values of the watch variables retrieved from the VCU to a list of watch frames. List <WatchFrame_t> watchFrameList = new List <WatchFrame_t>(); WatchFrame_t watchFrame; WatchElement_t watchElement; for (int frameIndex = 0; frameIndex < sampleCount; frameIndex++) { watchFrame = new WatchFrame_t(); watchFrame.CurrentDateTime = startTime.AddMilliseconds(frameIndex * frameIntervalMs); watchFrame.WatchElements = new WatchElement_t[watchCount]; for (short watchElementIndex = 0; watchElementIndex < watchCount; watchElementIndex++) { watchElement = new WatchElement_t(); watchElement.ElementIndex = watchElementIndex; watchElement.WatchIdentifier = workset.WatchElementList[watchElementIndex]; watchElement.DataType = dataTypes[translate[watchElementIndex]]; watchElement.Value = (double)values[(frameIndex * watchCount) + translate[watchElementIndex]]; watchFrame.WatchElements[watchElementIndex] = watchElement; } watchFrameList.Add(watchFrame); } return(watchFrameList); }
/// <summary> /// Poll the target hardware for watch values and store the retrieve values in the log cyclic queue. If the <c>Record</c> property is asserted also store /// the watch values in the cyclic queue used to store recorded data. /// </summary> /// <remarks>Runs on the underlying thread.</remarks> public override void Run() { try { m_CommunicationFault = false; while (StopThread == false) { if (Pause == false) { PauseFeedback = false; m_PollScheduler.Wait(IntervalMsUpdate); if (Pause == true) { m_Watchdog++; continue; } WatchFrame_t watchFrame; watchFrame = new WatchFrame_t(); watchFrame.CurrentDateTime = DateTime.Now; // Get the watch values from the target. try { m_Watchdog++; watchFrame.WatchElements = m_CommunicationInterface.UpdateWatchElements(true); m_ReadTimeoutCountdown = ReadTimeoutCountdown; } catch (CommunicationException) { // Don't assert the communication fault flag until the countdown has elapsed. if (m_ReadTimeoutCountdown <= 0) { // Assert the CommunicationFault property. m_CommunicationFault = true; // Close the communication Port. m_CommunicationInterface.CloseCommunication(m_CommunicationInterface.CommunicationSetting.Protocol); // Keep the watchdog ticking over so that the client can determine whether the port has locked. //DAS TODO when a comm fault occurs this loop never exits and the thread runs forever and is //not properly disposed. Will need to fix and other similar classes do { m_Watchdog++; Thread.Sleep(SleepMsRefreshWatchdog); }while (m_CommunicationFault == true); } else { m_ReadTimeoutCountdown--; continue; } } m_CommunicationFault = false; // Keep a count of the number of packets received. Used as a thread-safe way of blinking the packet received icon on the main window. This value // is read by the display update method on the main thread and provided it has incremented since the timeout last expired it will blink the icon. m_PacketCount++; // If currently in record mode copy the data to the cyclic buffer. if (Record == true) { #region - [Recording] - // Lock the cyclic queue and write the new data to the queue. lock (m_CyclicQueueRecord.SyncRoot) { m_CyclicQueueRecord.Enqueue(watchFrame); } #region - [AutoScale] - double valueCurrent; m_MutexAutoScaleWatchValues.WaitOne(DefaultMutexWaitDurationMs, false); if (m_FirstRecordPass == true) { // Recording has just started, initialize both the maximum and minimum values to the current value. for (short watchElementIndex = 0; watchElementIndex < watchFrame.WatchElements.Length; watchElementIndex++) { valueCurrent = watchFrame.WatchElements[watchElementIndex].Value; m_AutoScaleWatchValues[watchElementIndex].MaximumRaw = valueCurrent; m_AutoScaleWatchValues[watchElementIndex].MinimumRaw = valueCurrent; } m_FirstRecordPass = false; } else { // Keep the maximum and minimum value for each watch element up to date. for (short watchElementIndex = 0; watchElementIndex < watchFrame.WatchElements.Length; watchElementIndex++) { valueCurrent = watchFrame.WatchElements[watchElementIndex].Value; if (valueCurrent > m_AutoScaleWatchValues[watchElementIndex].MaximumRaw) { m_AutoScaleWatchValues[watchElementIndex].MaximumRaw = valueCurrent; } else if (valueCurrent < m_AutoScaleWatchValues[watchElementIndex].MinimumRaw) { m_AutoScaleWatchValues[watchElementIndex].MinimumRaw = valueCurrent; } } } m_MutexAutoScaleWatchValues.ReleaseMutex(); #endregion - [AutoScale] - // Update the record count up to the size of the cyclic buffer. Used to keep track of progress. if (m_RecordCount < m_CyclicQueueRecord.Size) { m_RecordCount++; } #region - [Queue Full] - // --------------------------- // Check if the queue is full. // --------------------------- if (m_CyclicQueueRecord.Count >= m_CyclicQueueRecord.Size) { // TODO - ThreadPollWatch.Run(). Implement code which archives the current log after 30 minutes of recording. /* * // -------------------------------------------- * // Still to be implemented. * // -------------------------------------------- * lock (m_CyclicQueueRecord.SyncRoot) * { * // ------------------------------ * // Save the current cyclic queue to disk. * // ------------------------------ * // Creates a SORTED array whose size is equal to the number of entries in the cyclic queue. * cyclicQueueRecordArray = m_CyclicQueueRecord.ToArray(); * * // Clear the cyclic buffer. * m_CyclicQueueRecord.Clear(); * } * * * // The save process must be carried out on a separate thread to enable data collection to continue without * // loosing packets. * m_ThreadSaveArrayToDisk = new Thread(SaveArrayToDisk); * * // The parameters to SaveArrayToDisk must be passed as an array of objects in this case. * //m_ThreadSaveArrayToDisk.Start(new object[] { m_FullyQualifiedFileMnemonic + "." + m_Page.ToString() + CommonConstants.ExtensionWatchFile, cyclicQueueRecordArray }); * m_ThreadSaveArrayToDisk.Start(new object[] { m_FullyQualifiedFilename + CommonConstants.ExtensionWatchFile, cyclicQueueRecordArray, m_AutoScaleWatchValues }); * * // Increment the page number. * m_Page++; * * // Reset the progress bar. * m_ApplicationProgressBarValue = 0; * * // Check if the maximum number of pages has been exceeded, if so, end the recording. * if (m_Page > Parameter.PageReferenceMax) * { * // Do the processing on a separate thread. * Thread threadEndRecording = new Thread(EndRecording); * threadEndRecording.Start(); * } */ } #endregion - [Queue Full] - #endregion - [Recording] - } // Record the data to the simulated fault log cyclic buffer. lock (m_CyclicQueueLog.SyncRoot) { m_CyclicQueueLog.Enqueue(watchFrame); } // Write the new data to the lookup table. m_CommunicationInterface.UpdateWatchVariableTable(watchFrame.WatchElements); } else { PauseFeedback = true; m_Watchdog++; Thread.Sleep(SleepMsCheckPause); } } } finally { base.Run(); } }