using System.Globalization; namespace InnovEnergy.Lib.Devices.BatteryDeligreen; using System; using System.Collections.Generic; public class TelecommandFrameParser { private static Int32 _currentIndex; private const Int32 FrameLength = 232; private static readonly Dictionary ByteAlarmCodes = new() { { "00", "Normal, no alarm" }, { "01", "Alarm that analog quantity reaches the lower limit" }, { "02", "Alarm that analog quantity reaches the upper limit" }, { "F0", "Other alarms" } }; public BatteryDeligreenAlarmRecord? ParsingTelecommandFrame(String response) { _currentIndex = 0; // Reset currentIndex to the start if (string.IsNullOrEmpty(response) || response.Length < FrameLength) { Console.WriteLine("Response is too short to contain valid data."); Console.WriteLine(" Fixed Length" + FrameLength); Console.WriteLine(" response Length" + response.Length); return null; } // Check starting byte string startingByte = response.Substring(_currentIndex, 2).ToUpper(); if (startingByte == "7E") { // Console.WriteLine($"Starting byte: {startingByte} (Hex)"); } else { Console.WriteLine($"Incorrect starting byte: {startingByte}"); return null; } _currentIndex += 2; // Extract firmware version var versionBytes = response.Substring(_currentIndex, 4); try { var versionAscii = HexToAscii(versionBytes); // Console.WriteLine($"Firmware version: {versionBytes} (Hex), ASCII: {versionAscii}"); } catch (Exception) { Console.WriteLine($"Failed to decode firmware version from bytes: {versionBytes}"); return null; } _currentIndex += 4; // Extract and parse other fields ParseAndPrintHexField(response, "Device Address", 4); // this is not added to the Alarm record ParseAndPrintHexField(response, "Device Code (CID1)", 4); // this is not added to the Alarm record ParseAndPrintHexField(response, "Function Code", 4); // this is not added to the Alarm record ParseAndPrintHexField(response, "Length Code", 8); // this is not added to the Alarm record ParseAndPrintHexField(response, "Data Flag", 4); // this is not added to the Alarm record ParseAndPrintHexField(response, "Command Group", 4); // this is not added to the Alarm record ParseAndPrintHexField(response, "Number of Cells", 4); // this is not added to the Alarm record // Parse Cell Temperature Alarm fields var cellAlarmList = ExtractCellAlarm(response); // Parse Number of Temperature Alarm ParseAndPrintHexField(response, "Number of Temperature ", 4); // this is not added to the Alarm record // Parse Cell Temperature Alarm fields var cellTemperatureAlarm = ExtractCellTempAlarm(response); var enviTempAlarm = ParseAndPrintField(response, "Environment Temperature Alarm" ); var powerTempAlarm = ParseAndPrintField(response, "Power Temperature Alarm" ); var currentAlarm = ParseAndPrintField(response, "Charge/Discharge Current Alarm" ); var totalVoltageAlarm = ParseAndPrintField(response, "Total Battery Voltage Alarm" ); var batteryAlarmRecord = new BatteryDeligreenAlarmRecord(cellAlarmList, cellTemperatureAlarm, enviTempAlarm, powerTempAlarm,currentAlarm, totalVoltageAlarm); return batteryAlarmRecord; } private static List ExtractCellTempAlarm(String response) { var cellTempAlarmList = new List(); var tempCellAlarm = response.Substring(_currentIndex, 4); for (var i = 0; i < 4; i++) { try { var tempAscii = HexToAscii(tempCellAlarm); var alarmMessage = ByteAlarmCodes.ContainsKey(tempAscii) ? ByteAlarmCodes[tempAscii] : "Unknown alarm code"; cellTempAlarmList.Add(alarmMessage); } catch (Exception) { Console.WriteLine($"Failed to decode Cell Temp Alarm {i + 1} from bytes: {tempCellAlarm}"); } _currentIndex += 4; } return cellTempAlarmList; } private static List ExtractCellAlarm(String response) { var cellAlarmList = new List(); // Process Alarms for all 16 cells for (var i = 0; i < 16; i++) { var cellAlarm = response.Substring(_currentIndex, 4); try { var alarmAscii = HexToAscii(cellAlarm); var alarmMessage = ByteAlarmCodes.ContainsKey(alarmAscii) ? ByteAlarmCodes[alarmAscii] : "Unknown alarm code"; cellAlarmList.Add(alarmMessage); // Console.WriteLine($"Cell {i + 1}: Alarm Code {cellAlarm}, Status: {alarmMessage}"); } catch (Exception) { Console.WriteLine($"Failed to decode Cell Alarm {i + 1} from bytes: {cellAlarm}"); } _currentIndex += 4; } return cellAlarmList; } private static void ParseAndPrintHexField(String response, String fieldName, Int32 length) { var hexBytes = response.Substring(_currentIndex, length); try { var asciiValue = HexToAscii(hexBytes); var decimalValue = int.Parse(asciiValue, NumberStyles.HexNumber); //Console.WriteLine($"{fieldName}: {hexBytes} (Hex), ASCII: {asciiValue}, Decimal: {decimalValue}"); } catch (Exception) { Console.WriteLine($"Failed to decode {fieldName} from bytes: {hexBytes}"); } _currentIndex += length; } private static String ParseAndPrintField(String response, String fieldName) { var fieldBytes = response.Substring(_currentIndex, 4); var alarmMessage = "Failed to read alarm"; try { var tempAscii = HexToAscii(fieldBytes); alarmMessage = ByteAlarmCodes.ContainsKey(tempAscii) ? ByteAlarmCodes[tempAscii] : "Unknown alarm code"; } catch (Exception) { Console.WriteLine($"Failed to decode : {fieldName}" + " Alarm"); } _currentIndex += 4; return alarmMessage; } private static String HexToAscii(String hex) { var bytes = new Byte[hex.Length / 2]; for (var i = 0; i < hex.Length; i += 2) { bytes[i / 2] = byte.Parse(hex.Substring(i, 2), NumberStyles.HexNumber); } return System.Text.Encoding.ASCII.GetString(bytes); } private static double HexToDecimal(String hex) { return int.Parse(hex, NumberStyles.HexNumber); } }