/// <summary> /// Inserts a channel at the specified index. /// </summary> public void Insert( int index, Channel value ) { if (value == null || index >= List.Count) return; List.Insert( index, value ); }
/// <summary> /// Add a new <c>Channel</c> to the collection. /// </summary> /// <returns>The index of the added channel.</returns> public int Add(Channel channel) { if (channel == null) throw new ArgumentNullException (); return( List.Add( channel ) ); }
/// <summary> /// Removes the specified channel from the plotter. /// </summary> public void Remove ( Channel value ) { if (value == null) return; if (! List.Contains (value)) throw new ArgumentException (); for (int i = 0; i < List.Count; i ++) { if ( ((Channel) List[i]).YAxisName == value.YAxisName) { List.RemoveAt (i); break; } } }
/// <summary> /// Initializes a new instance of the class. /// </summary> /// <param name="channel">Channel reference.</param> public Cursor(Channel channel) { m_Channel = channel; }
/// <summary> /// Find the engineering value corresponding to the specified time. If an entry corresponding to the specified time does not exist, the value corresponding to /// the nearest valid time entry which is greater than the specified time is returned. /// </summary> /// <param name="timeInMs">The time, in ms, from the start of the plot.</param> /// <param name="channel">The channel reference that is currently being plotted.</param> /// <returns>The engineering value corresponding to the specified time or, if this does not exist, the value corresponding to the nearest valid time entry which /// is greater than the specified time. If no entry for the specified time exists and there are no valid time entries that are greater than the specified time /// Float.NaN is returned.</returns> protected float FindY(long timeInMs, Channel channel) { // The array index of the Points generic list where the specified time is located. int index; // Search the List for an entry containing the specified time. The BinarySearch() call returns the zero-based index of the entry corresponding to the // specified time, if it exists; otherwise, it will return a negative number that is the bitwise complement of the index of the entry corresponding to the // time that is the nearest to but greater than the specified time. If no entry is found, the bitwise complement of the Count is returned. index = channel.X.BinarySearch(timeInMs); // Default to 'Not a Number'. float y = float.NaN; if (index >= 0) { // An entry matching the specified time was found, return the corresponding Y value. y = channel.Points[index].Y; } else { // Check whether a valid entry exists. if (index != ~channel.X.Count) { // Get the Y value associated with the entry corresponding to the time that that is nearest to but greater than the specified time. y = channel.Points[~index].Y; } else { // Do nothing, y stays NaN. } } return y; }
/// <summary> /// Convert the specified pixel position into the corresponding PointF.X (time, in ms, since the start of the plot) and PointF.Y /// (engineering value) for the specified channel. Used when the mouse coordinates need to be converted to real values. /// </summary> /// <remarks> /// GraphArea.Top, GraphArea.Left corresponds to the pixel coordinates 0,0. /// </remarks> /// <param name="channel">The channel reference.</param> /// <param name="xInPixel">The X pixel value.</param> /// <param name="yInPixel">The Y pixel value.</param> /// <returns>A PointF value containing the converted time, in ms, from the start of the plot and the corresponding engineering value.</returns> protected PointF GetValueFromPixel(Channel channel, int xInPixel, int yInPixel) { // yInValue. float yAbsolute = (float)(m_GraphArea.Bottom - yInPixel) / (float)m_GraphArea.Height; float yRange = channel.MaximumValue - channel.MinimumValue; float yInValue = channel.MinimumValue + (yAbsolute * yRange); // xInValue. float xOffsetPixel = (float)(xInPixel - m_GraphArea.Left) / (float)m_GraphArea.Width; int xRangeInMs = (int)(m_XRange.Duration().Ticks / TimeSpan.TicksPerMillisecond); float xInValue = (xOffsetPixel * (float)xRangeInMs); return new PointF(xInValue, yInValue); }
/// <summary> /// Convert the PointF.X and PoinF.Y values (time and engineering value) for a particular channel into the relative pixel values for the GraphArea. /// </summary> /// <remarks> /// GraphArea.Top, GraphArea.Left corresponds to the pixel coordinates 0,0. /// </remarks> /// <param name="channel">The channel reference.</param> /// <param name="xInValue">The PointF.X value i.e. time, in ms, since the start of the plot.</param> /// <param name="yInValue">The PointF.Y value i.e. engineering value.</param> /// <returns>The pixel coordinates.</returns> protected Point GetPixelFromValue(Channel channel, int xInValue, float yInValue) { // yInPixel. float yRange = channel.MaximumValue - channel.MinimumValue; float y = (yInValue - channel.MinimumValue) / yRange; int yInPixel = m_GraphArea.Bottom - (int)(y * (float)m_GraphArea.Height); // xInPixel. int xOffsetValue = xInValue; xOffsetValue -= (m_LeftDisplayLimit); int xRangeInMs = (int)(m_XRange.Duration().Ticks / TimeSpan.TicksPerMillisecond); float xOffsetAbs = (float)((float)xOffsetValue / (float)xRangeInMs); int xInPixel = m_GraphArea.Left + (int)(xOffsetAbs * m_GraphArea.Width); return new Point(xInPixel, yInPixel); }
/// <summary> /// Plot the points contained within the specified ArrayList[]. Each element of ArrayList[] contains a continuous set of points to be plotted. /// If there are no gaps in the data to be plotted the array will contain a single element. /// </summary> /// <param name="graphics">Reference to the The GDI+ drawing surface.</param> /// <param name="activeChannel">True, if the specified channel is the active channel; otherwise false.</param> /// <param name="channel">The channel reference.</param> /// <param name="pointsList">An array of ArrayLists where each element of the array contains the points to be plotted for each continuous block of data.</param> private void PlotPointsListArray(Graphics graphics, bool activeChannel, Channel channel, ArrayList[] pointsList) { // Skip if the Dispose() method has been called. if (IsDisposed) { return; } // Convert the ArrayList[] of pixel coordinates to a jagged array i.e. an array whose elements are an array, ready for plotting. Point[][] pointsToPlot = new Point[channel.BreakPoint.Count + 1][]; for (int block = 0; block <= channel.BreakPoint.Count; block++) { pointsToPlot[block] = new Point[pointsList[block].Count]; for (int index = 0; index < pointsList[block].Count; index++) { pointsToPlot[block][index] = (Point)pointsList[block][index]; } } graphics.SetClip(m_GraphArea); try { // Plot each block of pixel coordinates. for (int block = 0; block <= channel.BreakPoint.Count; block++) { // Skip if there aren't at least 2 points to plot. if (pointsToPlot[block].Length < 2) { continue; } if (activeChannel) { graphics.DrawLines(new Pen(channel.ChannelColor, 1.5F), pointsToPlot[block]); } else { graphics.DrawLines(new Pen(channel.ChannelColor, 1.5F), pointsToPlot[block]); } } } catch (Exception) { // ------------------------------------------------- // Catch the exception and report this to the user. // ------------------------------------------------- m_InvalidData = true; string statusMessage = Resources.SMPlotInvalid; SizeF stringSize = graphics.MeasureString(statusMessage, Font); int top = m_GraphArea.Bottom - Font.Height - StatusMessageBorder; int left = (int)((m_GraphArea.Width - (int)stringSize.Width) / 2); using (Brush textBrush = new SolidBrush(Color.Red)) { graphics.DrawString(statusMessage, Font, textBrush, left, top); } } }
/// <summary> /// Create a jagged array of ArrayLists containing the points to be plotted for each block of data. /// </summary> /// <param name="channel">The channel reference.</param> /// <returns>An array of ArrayLists containing the points to be plotted for each block.</returns> private ArrayList[] AddToPointsListArray(Channel channel) { // Create an ArrayList of PointF values for each group of PointF values separated by a break point e.g. zero breakpoints would // require a single list, 1 break point would require two lists etc, where each break point specifies the elapsed time value // corresponding to a plot entry where the pen colour is to be set to transparent between this particular plot entry and the next // plot entry. Break points are associated with min/hour/day logs and RTD logs and record periods where the unit has been powered down. ArrayList[] pointsList = new ArrayList[channel.BreakPoint.Count + 1]; for (int block = 0; block <= channel.BreakPoint.Count; block++) { pointsList[block] = new ArrayList(); } // Local variable to store the current entry of the generic list associated with the channel. PointF pointStored = new PointF(); // Local variable to store the pixel value corresponding to the current entry of the generic list associated with te channel. Point p; if (m_CurrentState == PlotterState.Stopped) { // The index of the current block of data. int blockIndex = 0; for (int index = 0; index < channel.Points.Count; index++) { // Get the current entry of the generic list. pointStored = channel.Points[index]; // Convert the PointF values to pixel coordinates. p = GetPixelFromValue(channel, (int)pointStored.X, pointStored.Y); pointsList[blockIndex].Add(p); // Check if the generic list contains further breakpoints. if (blockIndex < channel.BreakPoint.Count) { // If the current time value corresponds to a latest break point, add the remaining data values to the next // pointsList element. if (pointStored.X == channel.BreakPoint[blockIndex]) { pointsList[blockIndex].Add(p); blockIndex++; } } } } else { // Note: There are no break points associated with real time mode. for (int index = 0; index < channel.Points.Count - m_PointsToRemove; index++) { pointStored = (PointF)channel.Points[index + m_PointsToRemove]; // Convert the PointF values to pixel coordinates. p = GetPixelFromValue(channel, (int)pointStored.X, pointStored.Y); pointsList[0].Add(p); } } return pointsList; }
/// <summary> /// Plot the points contained within the specified ArrayList[]. Each element of ArrayList[] contains a continuous set of points to be plotted. /// If there are no gaps in the data to be plotted the array will contain a single element. /// </summary> /// <param name="graphics">Reference to the The GDI+ drawing surface.</param> /// <param name="channel">The channel reference.</param> /// <param name="pointsList">An array of ArrayLists where each element of the array contains the points to be plotted for each continuous block of data.</param> private void PlotPointsListArray(Graphics graphics, Channel channel, ArrayList[] pointsList) { // Skip if the Dispose() method has been called. if (IsDisposed) { return; } // Convert the ArrayList[] of pixel coordinates to a jagged array i.e. an array whose elements are an array, ready for plotting. Point p; Point[][] pointsToPlot = new Point[channel.BreakPoint.Count + 1][]; for (int block = 0; block <= channel.BreakPoint.Count; block++) { pointsToPlot[block] = new Point[pointsList[block].Count]; for (int index = 0; index < pointsList[block].Count; index++) { pointsToPlot[block][index] = (Point)pointsList[block][index]; } } graphics.SetClip(m_GraphArea); // Plot each block of pixel coordinates. Point pointFloatRepresentationOfSet = GetPixelFromValue(channel, 0, Channel.FloatRepresentationOfSet); // The pen colour. try { Color color; for (int block = 0; block <= channel.BreakPoint.Count; block++) { for (int index = 1; index < pointsToPlot[block].Length; index++) { // Skip if there aren't at least 2 points to plot. if (pointsToPlot[block].Length <= 1) { continue; } p = pointsToPlot[block][index]; // Set the pen colour depending on the active alarm state of the plot and the current value; clear: green, alarm: red. if (m_AlarmState == true) { color = (p.Y == pointFloatRepresentationOfSet.Y) ? m_AlarmStatePlotColor : m_ClearStatePlotColor; } else { color = (p.Y == pointFloatRepresentationOfSet.Y) ? m_ClearStatePlotColor : m_AlarmStatePlotColor; } graphics.DrawLine(new Pen(color, 1.5F), pointsToPlot[block][index - 1], pointsToPlot[block][index]); } } } catch (Exception) { // ------------------------------------------------- // Catch the exception and report this to the user. // ------------------------------------------------- m_InvalidData = true; string statusMessage = Resources.SMPlotInvalid; SizeF stringSize = graphics.MeasureString(statusMessage, Font); int top = m_GraphArea.Bottom - Font.Height - StatusMessageBorder; int left = (int)((m_GraphArea.Width - (int)stringSize.Width) / 2); using (Brush textBrush = new SolidBrush(Color.Red)) { graphics.DrawString(statusMessage, Font, textBrush, left, top); } } return; }
/// <summary> /// Create an array of ArrayLists containing the points to be plotted for each block of data. This method also adds the required points /// into the ArrayList[] so that the digital state transitions appear as a vertical line. /// </summary> /// <param name="channel">The channel reference.</param> /// <returns>An array of ArrayLists containing the points to be plotted for each block.</returns> private ArrayList[] AddToPointsListArray(Channel channel) { // Create an ArrayList of PointF values for each group of PointF values separated by a break point e.g. zero breakpoints would // require a single list, 1 break point would require two lists etc, where each break point specifies the elapsed time value // corresponding to a plot entry where the pen colour is to be set to transparent between this particular plot entry and the next // plot entry. Break points are associated with min/hour/day logs and RTD logs and record periods where the unit has been powered down. ArrayList[] pointsList = new ArrayList[channel.BreakPoint.Count + 1]; for (int block = 0; block <= channel.BreakPoint.Count; block++) { pointsList[block] = new ArrayList(); } // ----------------------------------------------------------------------------------------------------------------- // Populate the ArrayList[] with the points to be plotted. For state transitions don't draw directly to the next point, instead, make transition edges // vertical. Also only include points where transitions occur, this improves the processing time of the method. // ----------------------------------------------------------------------------------------------------------------- // Local variable to store the current element of the generic list of Points associated with the channel. PointF pointStored = new PointF(); // Local variable to store the pixel value corresponding to the current element of the generic list of Points associated with the channel. Point p; // Local variable to store the previous element of the generic list of Points associated with the channel. PointF pointStoredPrev = new PointF(); if (m_CurrentState == PlotterState.Stopped) { // The index of the current block of data. int blockIndex = 0; // True, if this is the first entry of the generic list; otherwise, false. bool firstEntry = true; // True, if this is the first entry of a new block; bool newBlock = false; for (int index = 0; index < channel.Points.Count; index++ ) { // Get the current entry of the generic list. pointStored = channel.Points[index]; #region - First Entry - // Check whether this is the first entry in the generic list. if (firstEntry) { firstEntry = false; // Add the pixel value corresponding to the first entry of the generic list to the ArrayList[]. p = GetPixelFromValue(channel, (int)pointStored.X, pointStored.Y); pointsList[blockIndex].Add(p); // Check if the generic list contains further breakpoints. if (blockIndex < channel.BreakPoint.Count) { // If the current time value corresponds to a break point increment the block index. if (pointStored.X == channel.BreakPoint[blockIndex]) { blockIndex++; // Ensure that the next entry in the generic list is recorded to the next ArrayList[] element. newBlock = true; } } } #endregion - First Entry - #region - Last Entry - else if (index == channel.Points.Count - 1) // Check whether this is the last entry in the generic list. { // Add the pixel value corresponding to the current entry of the generic list to the ArrayList[] and include an interim plot value so that // the transition is drawn as a vertical line if a state change has occurred. if (pointStored.Y != pointStoredPrev.Y) { // Add interim plot value so that transition is drawn as a vertical line. p = GetPixelFromValue(channel, (int)pointStored.X, pointStoredPrev.Y); pointsList[blockIndex].Add(p); } p = GetPixelFromValue(channel, (int)pointStored.X, pointStored.Y); pointsList[blockIndex].Add(p); } #endregion - Last Entry - #region - New Block - else if (newBlock) { newBlock = false; // Add the pixel value corresponding to the current entry of the generic list to the ArrayList[] and include an interim plot value so that the // transition is drawn as a vertical line if a state change has occurred. if (pointStored.Y != pointStoredPrev.Y) { // Add interim plot value so that transition is drawn as a vertical line. p = GetPixelFromValue(channel, (int)pointStored.X, pointStoredPrev.Y); pointsList[blockIndex].Add(p); } p = GetPixelFromValue(channel, (int)pointStored.X, pointStored.Y); pointsList[blockIndex].Add(p); // Check if the generic list contains further breakpoints. if (blockIndex < channel.BreakPoint.Count) { // If the current time value corresponds to a break point increment the block index. if (pointStored.X == channel.BreakPoint[blockIndex]) { blockIndex++; // Ensure that the next entry in the generic list is recorded to the next ArrayList[] element. newBlock = true; } } } #endregion - New Block - #region - Process Entry - else { // Only record values where the state of the digital IO has changed. if (pointStored.Y != pointStoredPrev.Y) { // A transition has occurred, add the interim plot value so that transition is drawn as a vertical line. p = GetPixelFromValue(channel, (int)pointStored.X, pointStoredPrev.Y); pointsList[blockIndex].Add(p); p = GetPixelFromValue(channel, (int)pointStored.X, pointStored.Y); pointsList[blockIndex].Add(p); // Check if the generic list contains further breakpoints. if (blockIndex < channel.BreakPoint.Count) { // If the current time value corresponds to a break point increment the block index. if (pointStored.X == channel.BreakPoint[blockIndex]) { blockIndex++; newBlock = true; } } } else { if (blockIndex < channel.BreakPoint.Count) // Check if the generic list contains further breakpoints. { // If the current time value corresponds to a break point increment the block index and add the pixel value corresponding to the // current entry of the generic list to the ArrayList[]. if (pointStored.X == channel.BreakPoint[blockIndex]) { // Add the pixel value corresponding to the current entry of the generic list to the ArrayList[]. p = GetPixelFromValue(channel, (int)pointStored.X, pointStored.Y); pointsList[blockIndex].Add(p); blockIndex++; newBlock = true; } } } } // Copy the current value to the previous value in order to detect state changes. pointStoredPrev = pointStored; #endregion - Process Entry - } } else { // -------------------------------------------------------------- // Note: There are no break points associated with real time mode. // -------------------------------------------------------------- for (int j = 0; j < channel.Points.Count - m_PointsToRemove; j++) { pointStored = (PointF)channel.Points[j + m_PointsToRemove]; if ((j != 0) && (pointStoredPrev.Y != pointStored.Y)) { // Transition has occurred, add interim plot value so that transition is drawn as a vertical line. p = GetPixelFromValue(channel, (int)pointStored.X, pointStoredPrev.Y); pointsList[0].Add(p); } p = GetPixelFromValue(channel, (int)pointStored.X, pointStored.Y); pointsList[0].Add(p); pointStoredPrev = pointStored; } } return pointsList; }
/// <summary> /// Checks whether the specified channel is contained within the collection. /// </summary> /// <param name="channel">Reference to the channel that is to be checked.</param> /// <returns>True, if the specified reference is contained within the collection; otherwise, false.</returns> public bool Contains( Channel channel ) { // If value is not of type Channel, this will return false. return( List.Contains( channel ) ); }
/// <summary> /// Returns the index of the specified channel. /// </summary> /// <returns>The index of the specified channel.</returns> public int IndexOf( Channel value ) { return( List.IndexOf( value ) ); }