//****************************************************************************************************** // StringExtensions.cs - Gbtc // // Copyright © 2017, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may // not use this file except in compliance with the License. You may obtain a copy of the License at: // // http://www.opensource.org/licenses/MIT // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 06/21/2017 - F. Russell Robertson // Created original version of source code. // //****************************************************************************************************** using System; namespace GSF.SELEventParser { // Implements the SEL method for checking the byte-wise integrity of each line in cev files public class ByteSum { #region [ Members ] // Constants const char separator = ','; // Fields private ulong m_byteSumValue; private string m_byteSumHexValue; private string m_lineData; private string m_lineByteSum; private bool m_match; private Action m_loggingFunction; #endregion #region [ Constructors ] public ByteSum() : this(message => { }) { } public ByteSum(Action loggingFunction) { if ((object)loggingFunction == null) throw new ArgumentNullException(nameof(loggingFunction)); m_loggingFunction = loggingFunction; } #endregion #region [ Properties ] /// /// Gets/Sets the computed sum-of-the-bytes (ByteSum) as a truncated (4 char) hex value /// public string HexValue { get { return m_byteSumHexValue; } set { m_byteSumHexValue = value; } } /// /// Gets/Sets the computed sum-of-the-bytes (BtyeSum) as a unsigned 64 bit integer /// public ulong Value { get { return m_byteSumValue; } set { m_byteSumValue = value; } } /// /// True if the computed ByteSum matches the ByteSum provided at the end of the line /// public bool Match { get { return m_match; } set { m_match = value; } } /// /// Get/Sets the data portion of the line (all characters to the left of the provided ByteSum) /// public string SentData { get { return m_lineData; } set { m_lineData = value; } } /// /// Gets/Sets the BtyeSum provided at the end of the line /// public string SentHexValue { get { return m_lineByteSum; } set { m_lineByteSum = value; } } #endregion #region [ Methods ] /// /// Implements the SEL method for checking the byte-wise integrity of each line in cev files /// /// The source string to test that contains "data" and the "truncated ByteSum value in Hex" /// The file name or identifier. /// The fixed length of the hex value in the "line" string. /// Sets class properties. public void Check(string line, string fileIdentifier = "", int hexValueLength = 4) { if (hexValueLength <= 0) throw new ArgumentException(string.Format("ByteSum.Check hexValueLenth={0} is invalid.", hexValueLength.ToString())); string s = string.Empty; Match = false; Value = 0; HexValue = new string('0', hexValueLength); SentData = string.Empty; SentHexValue = string.Empty; if (string.IsNullOrEmpty(line)) { m_loggingFunction(string.Format("ByteSum check error. Requested a check of null or empty line. File:{0}", fileIdentifier)); Match = true; //by definition return; } string left = GetData(line, false); if (string.IsNullOrEmpty(left)) { m_loggingFunction(string.Format("ByteSum check error. Requested a check of a line that contains no data.", fileIdentifier)); Match = true; //by definition return; } SentData = left; Value = ComputeSum(left.ToCharArray()); HexValue = GetHexValue(Value, hexValueLength); string right = GetByteSum(line, true).Trim(); if (string.IsNullOrEmpty(right)) { m_loggingFunction(string.Format("ByteSum check error. No BtyeSum found to check in file:{0}. Line data:{1}", fileIdentifier, SentData)); return; } //if (right.Contains("FID")) //{ // log.Info(string.Format("No BtyeSum found to check. However, 'FID' in the bytesum position can be valid. File: {0} Line data:{1}", fileIdentifier, byteSum.SentData)); // byteSum.SentHexValue = right; // byteSum.Match = true; // return byteSum; //} if (!System.Text.RegularExpressions.Regex.IsMatch(right, @"\A\b[0-9a-fA-F]+\b\Z")) { m_loggingFunction(string.Format("ByteSum check error. ByteSum provided:\"{0}\" is not a valid hex value. File:{1} Line data:{2}", right, fileIdentifier, SentData)); return; } if (right.Length != hexValueLength) { m_loggingFunction(string.Format("ByteSum check error. ByteSum provided:\"{0}\" does not have the required length of {1}. File:{2} Line data:{3}", right, hexValueLength.ToString(), fileIdentifier, SentData)); return; } SentHexValue = right; Match = string.Equals(right, HexValue); if (!Match) m_loggingFunction(string.Format("ByteSum check error. Provided ByteSum:\"{0}\" Computed ByteSum:\"{1}\" File:{2} Line data:{3}", SentHexValue, HexValue, fileIdentifier, SentData)); return; } #endregion #region [ Static ] // Static Methods /// /// Returns the byte sum as a hex value (string) /// /// the length of the return string. Padded left with zero if short. Set to 0 to disable. Default 4. /// The sum as a hex value private static string GetHexValue(ulong value, int hexValueLength = 4) { string result = value.ToString("X").PadLeft(hexValueLength, '0'); return result.SubstringEnd(hexValueLength); } private static ulong ComputeSum(char[] inArray) { ulong total = 0; foreach (char c in inArray) { total += c; } return total; } private static string GetByteSum(string line, bool quoteUnwrap = true) { if (string.IsNullOrEmpty(line)) return string.Empty; if (line.IndexOf(separator) < 0) return string.Empty; string inStringRev = line.Reverse(); int pos = inStringRev.IndexOf(separator); if (quoteUnwrap) return line.Substring(line.Length - pos).ToUpper().Trim('\"'); return line.Substring(line.Length - pos).ToUpper().Trim(); } private static string GetData(string line, bool twoCSVvaluesRequired = true) { if (string.IsNullOrEmpty(line)) return string.Empty; if (line.IndexOf(separator) < 0 && twoCSVvaluesRequired) return string.Empty; if (line.IndexOf(separator) < 0) return line; string inStringRev = line.Reverse(); int pos = inStringRev.IndexOf(separator); return line.Substring(0, line.Length - pos); } #endregion } }