using System; using System.Globalization; using System.Text; using System.IO; namespace Wayne.FDCPOSInterface { public class SignedFuellingInfo { /// /// A class that calculates a CRC32 value from various sources. /// public class Crc32 { #region Fields private UInt32[] table; private UInt32 initialValue; private UInt32 crc; private UInt32 length; private byte[] tempBuffer; #endregion #region Construction /// /// Constructor. /// Initializes the CRC to 0xFFFFFFFF. /// public Crc32() : this(0xffffffff) { } /// /// Constructor. /// /// The initial value of the CRC. public Crc32(UInt32 initialValue) { this.initialValue = initialValue; UInt32 poly = 0xedb88320; table = new UInt32[256]; UInt32 temp = 0; for (UInt32 i = 0; i < table.Length; i++) { temp = i; for (UInt32 j = 8; j > 0; j--) { if ((temp & 1) == 1) temp = (UInt32)((temp >> 1) ^ poly); else temp >>= 1; } table[i] = temp; } Reset(); } #endregion #region Properties /// /// The CRC32 value. /// public UInt32 Value { get { return ~crc; } } /// /// The total length of the data. /// public UInt32 Length { get { return length; } } #endregion #region Methods /// /// Resets the CRC32 value. /// public void Reset() { crc = initialValue; length = 0; } /// /// Appends a byte array to the CRC32 value. /// /// The bytes to append. public void Append(byte[] bytes) { Append(bytes, 0, bytes.Length); } /// /// Appends a byte array to the CRC32 value. /// /// The bytes to append. /// The offset of the array of where to start. /// The number of bytes to read from the array. public void Append(byte[] bytes, int offset, int length) { this.length += (UInt32)length; for (int i = offset; i < length; i++) { byte index = (byte)(((crc) & 0xff) ^ bytes[i]); crc = (UInt32)((crc >> 8) ^ table[index]); } } /// /// Appends a text string to the CRC32 value. /// /// The text to append. public void Append(string text) { if (!string.IsNullOrEmpty(text)) Append(Encoding.Unicode.GetBytes(text)); } /// /// Appends the content of a file to the CRC32 value. /// /// The filename of the file to append. public void AppendFile(string filename) { AppendFile(filename, 16384); } /// /// Appends the content of a file to the CRC32 value. /// /// The filename of the file to append. /// The size of the read-buffer to use. public void AppendFile(string filename, int bufferSize) { try { if ((tempBuffer == null) || (bufferSize != tempBuffer.Length)) tempBuffer = new byte[bufferSize]; using (FileStream fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read)) { using (BinaryReader binaryReader = new BinaryReader(fileStream)) { int usedBufferSize; do { usedBufferSize = binaryReader.Read(tempBuffer, 0, bufferSize); if (usedBufferSize > 0) Append(tempBuffer, 0, usedBufferSize); } while (usedBufferSize == bufferSize); } } } catch { } } #endregion } public class Data { public DateTime DateTime { get; set; } public int PumpNumber { get; set; } public string ProductName { get; set; } public decimal TotalAmount { get; set; } public decimal Volume { get; set; } public decimal Price { get; set; } public string CurrencySymbol { get; set; } public bool CurrencySymbolAfterValue { get; set; } public bool SpaceBetweenCurrencySymbolAndValue { get; set; } public string VolumeUnit { get; set; } public int ControllerModuleCrc { get; set; } public int PrinterWidth { get; set; } } private const string Iv = "@BEPQRV]"; private readonly Crc32 crc32 = new Crc32((uint)( ((Iv[6] - Iv[0]) * (Iv[7] - Iv[0]) + (Iv[2] - Iv[0])) * 1024 * 1024 + ((Iv[3] - Iv[0]) * (Iv[5] - Iv[0]) + (Iv[1] - Iv[0])) * 1024 + ((Iv[6] - Iv[0]) * (Iv[7] - Iv[0]) + (Iv[4] - Iv[0])))); private const string SignChar = "^"; private const string BadSignReplaceChar = " "; private const string SignError = "\r\n^*PUMP INFO SIGN ERROR*^\r\n"; public string EncodeReceiptText(Data data) { StringBuilder result = new StringBuilder(); NumberFormatInfo numberFormatInfo = new NumberFormatInfo { NumberDecimalDigits = 2, NumberDecimalSeparator = "," }; if (data.PrinterWidth < 30) { // Design for 24 chars // =================== string preText = data.PrinterWidth > 24 ? new string(' ', data.PrinterWidth - 24) : string.Empty; //^ #12 [20100503085824] ^45697A3C result.Append(preText + GetSignedString(string.Concat(SignChar, " #", data.PumpNumber.ToString().PadRight(3), "[", data.DateTime.ToString("yyyyMMddHHmmss"), "] ", SignChar))); //^ Blyfri 95 ^2C5BD546 result.Append(preText + GetSignedString(string.Concat(SignChar, " ", data.ProductName.PadRight(21), SignChar))); //^ 563,74 DKK ^65B9F8A3 result.Append(preText + GetSignedString(string.Concat(SignChar, GetCurrencyString(numberFormatInfo, data).PadLeft(21), " ", SignChar))); //^ 54,52 L ^98C35479 result.Append(preText + GetSignedString(string.Concat(SignChar, data.Volume.ToString(numberFormatInfo).PadLeft(10), " ", data.VolumeUnit.PadRight(11), SignChar))); //^ @ 10,34 DKK/L ^465D9872 result.Append(preText + GetSignedString(string.Concat(SignChar, string.Concat("@ ", data.Price.ToString(numberFormatInfo)).PadLeft(10), " ", string.Concat(data.CurrencySymbol, "/", data.VolumeUnit).PadRight(11), SignChar))); //^ CS A8F0EC90:######## ^6A9C8FC4) result.Append(preText + GetSignedString(string.Concat(SignChar, " CS ", data.ControllerModuleCrc.ToString("X8"), " ######## ", SignChar))); } else { // Design for 30 chars // =================== string preText = data.PrinterWidth > 30 ? new string(' ', data.PrinterWidth - 30) : string.Empty; //123456789012345678901234567890 //^ #12 [20100503085824] ^4A7692AE result.Append(preText + GetSignedString(string.Concat(SignChar, " #", data.PumpNumber.ToString().PadRight(3), "[", data.DateTime.ToString("yyyyMMddHHmmss"), "] ", SignChar))); //123456789012345678901234567890 //^ Unlead 95 563,74 SEK ^3CE18720 result.Append(preText + GetSignedString(string.Concat(SignChar, " ", data.ProductName.PadRight(14), GetCurrencyString(numberFormatInfo, data).PadLeft(12), " ", SignChar))); //123456789012345678901234567890 //^ 54,52 L @ 10,34 SEK/L ^0A1B0FB2 result.Append(preText + GetSignedString(string.Concat(SignChar, " ", string.Concat(data.Volume.ToString(numberFormatInfo), " ", data.VolumeUnit, " @ ", data.Price.ToString(numberFormatInfo), " ", data.CurrencySymbol, "/", data.VolumeUnit).PadRight(27), SignChar))); //123456789012345678901234567890 //^ C/S A8F0EC90:######## ^05198FB7 result.Append(preText + GetSignedString(string.Concat(SignChar, " C/S ", data.ControllerModuleCrc.ToString("X8"), " ######## ", SignChar))); } return result.ToString(); } public string DecodeReceiptText(string text, int printerModuleCrc) { bool hasError = false; int offset = 0; int startCharIndex, endCharIndex; do { startCharIndex = text.IndexOf(SignChar, offset); if (startCharIndex > -1) { endCharIndex = text.IndexOf(SignChar, startCharIndex + 1); if (endCharIndex == -1) { text = text.Replace(SignChar, BadSignReplaceChar); hasError = true; } else { if (text.Length <= endCharIndex + 8) { text = text.Remove(startCharIndex, 1).Insert(startCharIndex, BadSignReplaceChar).Remove(endCharIndex, 1).Insert(endCharIndex, BadSignReplaceChar); hasError = true; } else { string crcString = text.Substring(endCharIndex + 1, 8); string signedText = text.Substring(startCharIndex, endCharIndex - startCharIndex + 1); if (!crcString.Equals(GetStringSignValue(signedText), StringComparison.InvariantCultureIgnoreCase)) { text = text.Remove(startCharIndex, 1).Insert(startCharIndex, BadSignReplaceChar).Remove(endCharIndex, 1).Insert(endCharIndex, BadSignReplaceChar); hasError = true; } int printerModuleCrcIndex = signedText.IndexOf("########"); if (printerModuleCrcIndex > -1) text = text.Remove(startCharIndex + printerModuleCrcIndex, 8).Insert(startCharIndex + printerModuleCrcIndex, printerModuleCrc.ToString("X8")); text = text.Remove(endCharIndex + 1, 8); } offset = endCharIndex + 1; } } else endCharIndex = -1; } while ((startCharIndex > -1) && (endCharIndex > -1)); if (hasError) return text + SignError; return text; } private static string GetCurrencyString(IFormatProvider numberFormatInfo, Data data) { if (data.CurrencySymbolAfterValue) return string.Concat(data.TotalAmount.ToString(numberFormatInfo), data.SpaceBetweenCurrencySymbolAndValue ? " " : string.Empty, data.CurrencySymbol); return string.Concat(data.CurrencySymbol, data.SpaceBetweenCurrencySymbolAndValue ? " " : string.Empty, data.TotalAmount.ToString(numberFormatInfo)); } private string GetSignedString(string text) { return string.Concat(text, GetStringSignValue(text)); } private string GetStringSignValue(string text) { crc32.Reset(); crc32.Append(text); return crc32.Value.ToString("X8"); } } }