using LFP_Manager.DataStructure;
using LFP_Manager.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LFP_Manager.Function
{
    internal class CsRs485CommFunction124050
    {
        public const byte READ_COIL_STATUS = 0x01;
        public const byte READ_HOLDING_REG = 0x03;
        public const byte READ_INPUT_REG = 0x04;                 //Byul 구문 추가 필요
        public const byte FORCE_SINGLE_COIL = 0x05;
        public const byte PRESET_SINGLE_REG = 0x06;
        public const byte PRESET_MULTI_REG = 0x10;
        public const byte WRITE_COIL_REG = 0x0F;
        public const byte ERROR_REG = 0x90;

        public const byte READ_WRITE_REG = 0x17;
        public const byte SPECIAL_REG_02 = 0x19;
        public const byte READ_DEV_ID = 0x2B;

        public const byte FW_FLASH_ERASE_CMD = 0x43;
        public const byte FW_FLASH_WRITE_CMD = 0x31;

        public const byte NO_CMD = 0xFF;

        public static byte[] GetCRC(byte[] pby, int nSize)
        {
            ushort uIndex, i;
            ushort crc;
            byte uchCRCHi = 0xff;
            byte uchCRCLo = 0xff;
            byte[] result = new byte[2];

            for (i = 0; i < nSize; i++)
            {
                uIndex = (ushort)((int)uchCRCLo ^ (int)pby[i]);
                uchCRCLo = (byte)(uchCRCHi ^ csConstData.CRC_Data.auchCRCHi[uIndex]);
                uchCRCHi = csConstData.CRC_Data.auchCRCLo[uIndex];
            }
            crc = (ushort)((uchCRCHi << 8) | uchCRCLo);

            result[0] = (byte)(crc >> 8);
            result[1] = (byte)(crc >> 0);

            return result;
        }

        static public byte[] MakeReadRegisterData(ushort DevID, ushort cmd, ushort ReadAddr, ushort Size)
        {
            byte[] result = new byte[8];
            byte[] crc;

            result[0] = (byte)DevID;                // Device ID
            result[1] = (byte)cmd;     // Command
            result[2] = (byte)(ReadAddr >> 8);      // Register Address MSB
            result[3] = (byte)(ReadAddr >> 0);      // Register Address LSB
            result[4] = (byte)(Size >> 8);          // Count of Register MSB
            result[5] = (byte)(Size >> 0);          // Count of Register LSB

            crc = GetCRC(result, 6);

            result[6] = crc[1];                       // CRCH
            result[7] = crc[0];                       // CRCL

            return result;
        }

        static public byte[] MakeWriteCoilData(ushort DevID, ushort WriteAddr, short WriteData)
        {
            byte[] result = new byte[7 + (1 * 1) + 2];
            byte[] crc;
            ushort i = 0;

            result[i++] = (byte)DevID;                  // Device ID
            result[i++] = (byte)WRITE_COIL_REG;       // Command
            result[i++] = (byte)(WriteAddr >> 8);       // Register Address MSB
            result[i++] = (byte)(WriteAddr >> 0);       // Register Address LSB
            result[i++] = (byte)(1 >> 8);            // Count of Register MSB
            result[i++] = (byte)(1 >> 0);            // Count of Register LSB
            result[i++] = (byte)(1 * 1);             // Byte Count - [2 * (Num of register)]

            result[i++] = (byte)(WriteData >> 0);

            crc = GetCRC(result, i);

            result[i++] = crc[1];                       // CRCH
            result[i++] = crc[0];                       // CRCL

            return result;
        }

        static public byte[] MakeWriteRegisterData(ushort DevID, ushort WriteAddr, short WriteData)
        {
            byte[] result = new byte[9 + (1 * 2)];
            byte[] crc;
            ushort i = 0;

            result[i++] = (byte)DevID;                  // Device ID
            result[i++] = (byte)PRESET_MULTI_REG;       // Command
            result[i++] = (byte)(WriteAddr >> 8);       // Register Address MSB
            result[i++] = (byte)(WriteAddr >> 0);       // Register Address LSB
            result[i++] = (byte)(1 >> 8);            // Count of Register MSB
            result[i++] = (byte)(1 >> 0);            // Count of Register LSB
            result[i++] = (byte)(1 * 2);             // Byte Count - [2 * (Num of register)]

            result[i++] = (byte)(WriteData >> 8);
            result[i++] = (byte)(WriteData >> 0);

            crc = GetCRC(result, i);

            result[i++] = crc[1];                       // CRCH
            result[i++] = crc[0];                       // CRCL

            return result;
        }

        static public byte[] MakeReadDevIdRegReqData(ushort DevID, ushort cmd, ushort ReadAddr)
        {
            byte[] result = new byte[7];
            byte[] crc;

            result[0] = (byte)DevID;                // Device ID
            result[1] = (byte)cmd;                  // Command
            result[2] = (byte)0x0E;                 // MEI Type
            result[3] = (byte)0x02;                 // Read Dev Id code
            result[4] = (byte)ReadAddr;             // Object Id

            crc = GetCRC(result, 5);

            result[5] = crc[1];                       // CRCH
            result[6] = crc[0];                       // CRCL

            return result;
        }

        static short GetRegister(ushort reg_addr, ref DeviceParamData aParam)
        {
            short result = 0;

            switch (reg_addr)
            {
                //case 19: result = (short)(0 >> 16); break;		// 0021 : UTC TimeStamp MSB
                //case 20: result = (short)(58 >> 0); break;		// 0022 : UTC TimeStamp LSB
                //case 21: result = (short)0x1000; break;			// 0023 : Cell Balancing Flag
                //case 22: result = (short)0x0000; break;		// 0024 : Cell Balancing Voltage
                //case 23: result = (short)15; break;     // 0024 : Cell Balancing Time

                case 0x4002: result = (short)aParam.CellUnderVoltageWarning; break;     // 0061 : Low cell voltage warning data
                                                                                        //case 33: result = (short)param.sf1.voltage.CUV_Threshold; break;		// 0062 : Low cell voltage protection data
                                                                                        //case 34: result = (short)param.sf1.voltage.CUV_Recovery; break;		// 0063 : Low cell voltage recovery data
                                                                                        //case 35: result = (short)param.sf1.voltage.SUV_Warning; break;		// 0064 : Low voltage warning data
                                                                                        //case 36: result = (short)param.sf1.voltage.SUV_Threshold; break;		// 0065 : Low voltage protection data
                                                                                        //case 37: result = (short)param.sf1.voltage.SUV_Recovery; break;		// 0066 : Low voltage recovery data
                                                                                        //case 38: result = (short)param.sf1.voltage.COV_Warning; break;		// 0067 : Over cell voltage warning data
                                                                                        //case 39: result = (short)param.sf1.voltage.COV_Threshold; break;		// 0068 : Over cell voltage protection data
                                                                                        //case 40: result = (short)param.sf1.voltage.COV_Recovery; break;		// 0069 : Over cell voltage recovery data
                                                                                        //case 41: result = (short)param.sf1.voltage.SOV_Warning; break;		// 0070 : Over voltage warning data
                                                                                        //case 42: result = (short)param.sf1.voltage.SOV_Threshold; break;		// 0071 : Over voltage protection data
                                                                                        //case 43: result = (short)param.sf1.voltage.SOV_Recovery; break;		// 0072 : Over voltage recovery data

                    //case 44: result = (short)param.sf1.temperature.OT_Chg_Warning; break;		// 0044 : Charge over temperature warning data
                    //case 45: result = (short)param.sf1.temperature.OT_Chg_Threshold; break;	// 0045 : Charge over temperature protection data
                    //case 46: result = (short)param.sf1.temperature.OT_Chg_Recovery; break;	// 0046 : Charge over temperature recovery data
                    //case 47: result = (short)param.sf1.temperature.OT_Chg_Time; break;		// 0047 : Charge over temperature time

                    //case 48: result = (short)param.sf1.temperature.OT_Dsg_Warning; break;		// 0048 : Discharge over temperature warning data
                    //case 49: result = (short)param.sf1.temperature.OT_Dsg_Threshold; break;	// 0049 : Discharge over temperature protection data
                    //case 50: result = (short)param.sf1.temperature.OT_Dsg_Recovery; break;	// 0050 : Discharge over temperature recovery data
                    //case 51: result = (short)param.sf1.temperature.OT_Dsg_Time; break;		// 0051 : Discharge over temperature time
            }

            return result;
        }

        static byte[] ModBusGetRegWordToBytes(ushort addr, ref DeviceParamData aParam)
        {
            short data;
            byte[] result = new byte[2];

            data = GetRegister(addr, ref aParam);

            result[0] = (byte)(data >> 8);
            result[1] = (byte)(data >> 0);

            return result;
        }

        static public byte[] MakeWriteRegisterData(ushort DevID, ushort WriteAddr, ushort reg_len, ref DeviceParamData aParam)
        {
            int tlen = (reg_len * 2) + 7 + 2;
            byte[] result = new byte[tlen];
            byte[] tmp;
            byte[] crc;

            result[0] = (byte)DevID;                // Device ID
            result[1] = (byte)PRESET_MULTI_REG;     // Command
            result[2] = (byte)(WriteAddr >> 8);     // Register Address MSB
            result[3] = (byte)(WriteAddr >> 0);     // Register Address LSB
            result[4] = (byte)(reg_len >> 8);       // Count of Register MSB
            result[5] = (byte)(reg_len >> 0);       // Count of Register LSB
            result[6] = (byte)(reg_len * 2); ;       // Current Value MSB

            for (int i = 0; i < reg_len; i++)
            {
                tmp = ModBusGetRegWordToBytes((ushort)(WriteAddr + i), ref aParam);
                result[7 + (i * 2) + 0] = tmp[0];
                result[7 + (i * 2) + 1] = tmp[1];
            }
            crc = GetCRC(result, 8);

            result[tlen - 2] = crc[0];                       // CRCH
            result[tlen - 1] = crc[1];                       // CRCL

            return result;
        }

        static public byte[] MakeReadWriteRegisterData(ushort DevID, ushort WriteAddr, short WriteData)
        {
            byte[] result = new byte[15];
            byte[] crc;
            ushort i = 0;

            result[i++] = (byte)DevID;                  // Device ID
            result[i++] = (byte)READ_WRITE_REG;         // Command
            result[i++] = (byte)(WriteAddr >> 8);       // Read Register Address MSB
            result[i++] = (byte)(WriteAddr >> 0);       // Read Register Address LSB
            result[i++] = (byte)(0 >> 8);               // Read Count of Register MSB
            result[i++] = (byte)(0 >> 0);               // Read Count of Register LSB
            result[i++] = (byte)(WriteAddr >> 8);       // Write Register Address MSB
            result[i++] = (byte)(WriteAddr >> 0);       // Write Register Address LSB
            result[i++] = (byte)(1 >> 8);               // Write Count of Register MSB
            result[i++] = (byte)(1 >> 0);               // Write Count of Register LSB
            result[i++] = (byte)(1 * 2);                // Byte Count - [2 * (Num of register)]

            result[i++] = (byte)(WriteData >> 8);
            result[i++] = (byte)(WriteData >> 0);

            crc = GetCRC(result, i);

            result[i++] = crc[0];                       // CRCH
            result[i++] = crc[1];                       // CRCL

            return result;
        }

        public static byte[] MakeCheckSum(byte[] sData, int offset, int len, bool flag)
        {
            byte[] result = new byte[2];
            int checksum = 0;

            for (int i = 0; i < len; i++)
            {
                checksum += sData[i + offset];
            }

            checksum = ~checksum + 1;

            result[0] = (byte)(checksum >> 8);
            result[1] = (byte)checksum;

            return result;
        }

        private static byte[] MakeTxPacket(byte[] sData, int len)
        {
            string str = "";
            byte[] result;
            char[] chrArray;
            int checksum = 0;
            string checksumStr = "";
            char[] checksumChr;

            str = "~";

            for (int i = 0; i < len; i++)
            {
                str += sData[i].ToString("X2");
            }

            str += "\r";

            chrArray = str.ToCharArray();

            for (int i = 0; i < (chrArray.Length - 6); i++)
            {
                checksum += chrArray[i + 1];
            }

            checksum = ~checksum + 1;

            checksumStr = String.Format("{0:X2}{1:X2}", (byte)(checksum >> 8), (byte)checksum);
            checksumChr = checksumStr.ToCharArray();

            for (int i = 0; i < 4; i++)
            {
                chrArray[chrArray.Length - 5 + i] = checksumChr[i];
            }

            result = new byte[chrArray.Length];

            for (int i = 0; i < chrArray.Length; i++)
            {
                result[i] = (byte)chrArray[i];
            }

            return result;
        }

        public static byte[] LengthLchk(int sLen)
        {
            byte[] result = new byte[2];
            int lchksum = 0;

            lchksum = (~(((sLen >> 8) & 0xF) +
                         ((sLen >> 4) & 0xF) +
                         ((sLen >> 0) & 0xF)) + 1) % 16;
            lchksum = ((lchksum << 12) | sLen);

            result[0] = (byte)(lchksum >> 8);
            result[1] = (byte)(lchksum >> 0);

            return result;
        }

        public static byte[] MakeTxData(byte addr, byte cmd, int sLen)
        {
            int buffCh = 0;
            byte[] sData;
            byte[] lenId;
            byte[] checksum;

            sData = new byte[((sLen > 0) ? 11 : 10)];

            sData[buffCh] = 0x7E; buffCh++;                 // SOI
            sData[buffCh] = 0x25; buffCh++;                 // VER
            sData[buffCh] = addr; buffCh++;                 // ADDR
            sData[buffCh] = 0x46; buffCh++;                 // CID1
            sData[buffCh] = cmd; buffCh++;                  // CID2 (CMD)

            lenId = LengthLchk(sLen);                       // LENID
            sData[buffCh] = lenId[0]; buffCh++;             // LENID MSB
            sData[buffCh] = lenId[1]; buffCh++;             // LENID LSB

            if (sLen > 0)
            {
                sData[buffCh] = (byte)(sLen / 2); buffCh++;  // INFO
            }

            checksum = csSerialCommFunction.MakeCheckSum(sData, 1, sData.Length - 4, false);

            sData[buffCh] = checksum[1]; buffCh++;
            sData[buffCh] = checksum[0]; buffCh++;

            sData[buffCh] = 0x0D; buffCh++;                  // EOI

            return MakeTxPacket(sData, sData.Length);
        }

        public static int TbPacketCheck(byte[] rdata, int rlen)
        {
            int result = 0;
            byte[] cdata;
            byte[] checksum;
            byte[] checksum1;

            checksum = MakeCheckSum(rdata, 1, rlen - 6, false);

            checksum1 = new byte[2];
            checksum1[0] = csUtils.StrByte2toByte(rdata[rlen - 4], rdata[rlen - 5]);
            checksum1[1] = csUtils.StrByte2toByte(rdata[rlen - 2], rdata[rlen - 3]);


            if ((checksum[0] == checksum1[0]) && (checksum[1] == checksum1[1]))
            {
                cdata = csUtils.StrToByteArray(rdata, 0, rlen);

                result = 1;
            }

            return result;
        }

        static public int ModbusPacketFromSlaveCheck(byte[] rdata, ushort rlen)
        {
            int result = 0;
            byte[] cdata, crc;
            ushort clen, bytecount;

            if (rlen > 2)
            {
                cdata = rdata;

                switch (cdata[1])
                {
                    case READ_DEV_ID:
                        if (rlen < 10) break;
                        if (rdata[7] == 0) break;

                        int tmp = 0;
                        int cPos = 7;
                        int alen = 0;
                        for (int i = 0; i < cdata[7]; i++)
                        {
                            alen = cdata[cPos + 2];
                            cPos += alen + 2;
                            if (rlen < (cPos + 3))
                            {
                                tmp = 1;
                                break;
                            }
                        }
                        if (tmp == 1) break;

                        crc = GetCRC(cdata, (ushort)(rlen - 2));
                        if ((crc[1] == cdata[rlen - 2]) && (crc[0] == cdata[rlen - 1])) result = 1;
                        else result = -1;
                        break;
                    case READ_COIL_STATUS:
                    case READ_HOLDING_REG:
                    case READ_INPUT_REG:
                    case READ_WRITE_REG:
                        bytecount = cdata[2];
                        clen = (ushort)(bytecount + 5);	// header 3, tail 2
                        if (rlen >= clen)
                        {
                            crc = GetCRC(cdata, (ushort)(rlen - 2));
                            if ((crc[1] == cdata[rlen - 2]) && (crc[0] == cdata[rlen - 1])) result = 1;
                            else result = -1;
                        }
                        break;
                    case PRESET_MULTI_REG:
                    case FORCE_SINGLE_COIL:
                    case PRESET_SINGLE_REG:
                        clen = 8;
                        if (rlen >= clen)
                        {
                            crc = GetCRC(cdata, (ushort)(rlen - 2));
                            if ((crc[0] == cdata[rlen - 1]) && (crc[1] == cdata[rlen - 2])) result = 1;
                            else result = -1;
                        }
                        break;
                    case ERROR_REG:
                        clen = 6;
                        if (rlen >= clen)
                        {
                            crc = GetCRC(cdata, (ushort)(rlen - 2));
                            if ((crc[0] == cdata[rlen - 1]) && (crc[1] == cdata[rlen - 2])) result = 1;
                            else result = -1;
                        }
                        break;
                    case FW_FLASH_ERASE_CMD:
                        clen = 5;
                        if (rlen >= clen)
                        {
                            crc = GetCRC(cdata, (ushort)(rlen - 2));
                            if ((crc[0] == cdata[rlen - 1]) && (crc[1] == cdata[rlen - 2])) result = 2;
                            else result = -1;
                        }
                        break;
                    case FW_FLASH_WRITE_CMD:
                        clen = 5;
                        if (rlen >= clen)
                        {
                            crc = GetCRC(cdata, (ushort)(rlen - 2));
                            if ((crc[0] == cdata[rlen - 1]) && (crc[1] == cdata[rlen - 2])) result = 2;
                            else result = -1;
                        }
                        break;
                    default:
                        result = -1;
                        break;
                }
            }
            return result;
        }

        public static short[] SerialRxProcess(byte[] rData, ushort rRegAddr, ushort rLen, ref DeviceSystemData rSystemData)
        {
            short[] result = new short[2];

            switch (rData[1])
            {
                case READ_HOLDING_REG:      // 0x03
                    ReadHoldingRegisterProcess(rData, rRegAddr, rLen, ref rSystemData);
                    break;
                case READ_INPUT_REG:        // 0x04
                    ReadInputRegisterProcess(rData, rRegAddr, rLen, ref rSystemData);
                    break;
                case READ_WRITE_REG:        // 0x17
                    //ReadSP1RegisterProcess(rData, rRegAddr, rLen, ref rSystemData);
                    break;
                case SPECIAL_REG_02:        // 0x19
                    //ReadInputRegisterProcess(rData, rRegAddr, rLen, ref rSystemData);
                    break;
                case READ_DEV_ID:        // 0x2B
                    ReadDevIdRegisterProcess(rData, rRegAddr, rLen, ref rSystemData);
                    break;
                case PRESET_MULTI_REG:
                    //			read_holding_reg_process(reverse16(rsp->start_addr, true), reverse16(rsp->qty_reg, true));
                    //result[0] = 1;
                    //result[1] = 1;
                    break;
                case ERROR_REG:
                    result[0] = 2;
                    result[1] = (short)((rData[0] << 8) | rData[1]);
                    break;
            }
            return result;
        }

        private static void ReadCoilRegisterProcess(byte[] rData, ushort rRegAddr, ushort rLen, ref DeviceSystemData rSystemData)
        {
            int i, j;
            short reg_count;
            short reg_value;
            i = 2;
            reg_count = rData[i];

            i++;
            for (j = 0; j < reg_count; j++)
            {
                reg_value = (short)(rData[i + j]);
                SetCoilRegister((short)(rRegAddr + j), reg_value, ref rSystemData);
                i++;
            }
        }

        private static void ReadHoldingRegisterProcess(byte[] rData, ushort rRegAddr, ushort rLen, ref DeviceSystemData rSystemData)
        {
            int i, j;
            short reg_count;
            short reg_value;
            i = 2;
            reg_count = (short)(rData[i] / 2); i += 1;

            for (j = 0; j < reg_count; j++)
            {
                reg_value = (short)(rData[i] << 8 | rData[i + 1]);
                SetHoldingRegister((short)(rRegAddr + j), reg_value, ref rSystemData);
                i += 2;
            }
        }
        private static void ReadInputRegisterProcess(byte[] rData, ushort rRegAddr, ushort rLen, ref DeviceSystemData rSystemData)
        {
            int i, j;
            short reg_count;
            short reg_value;
            i = 2;
            reg_count = (short)(rData[i] / 2); i += 1;

            for (j = 0; j < reg_count; j++)
            {
                reg_value = (short)(rData[i] << 8 | rData[i + 1]);
                SetInputRegister((short)(rRegAddr + j), reg_value, ref rSystemData);
                i += 2;
            }
        }
        private static void ReadDevIdRegisterProcess(byte[] rData, ushort rRegAddr, ushort rLen, ref DeviceSystemData rSystemData)
        {
            int cPos = 7;
            int alen = 0;
            int regCount = rData[7];
            int oID = 0;

            for (int i = 0; i < regCount; i++)
            {
                oID = rData[cPos + 1];
                alen = rData[cPos + 2];
                byte[] tmp = new byte[alen];
                for (int j = 0; j < alen; j++) { tmp[j] = rData[cPos + 3 + j]; }

                switch (oID)
                {
                    case 0x00: rSystemData.Information.VendorName = Encoding.ASCII.GetString(tmp).Trim('\0'); break;
                    case 0x01: rSystemData.Information.ProductCode = Encoding.ASCII.GetString(tmp).Trim('\0'); break;
                    case 0x02: rSystemData.Information.MajorMinorRev = Encoding.ASCII.GetString(tmp).Trim('\0'); break;
                    case 0x05: rSystemData.Information.ModelName = Encoding.ASCII.GetString(tmp).Trim('\0'); break;
                    case 0x82: rSystemData.Information.HwSerialNumber = Encoding.ASCII.GetString(tmp).Trim('\0'); break;
                    case 0x83: rSystemData.Information.HwProductRev = Encoding.ASCII.GetString(tmp).Trim('\0'); break;
                    case 0x84: rSystemData.Information.ManufacturingDate = Encoding.ASCII.GetString(tmp).Trim('\0'); break;
                    case 0x86: rSystemData.Information.SwProductRev = Encoding.ASCII.GetString(tmp).Trim('\0'); break;
                }

                cPos += alen + 2;
                if (rLen < (cPos + 3))
                {
                    break;
                }
            }
        }
        private static void ReadSP2RegisterProcess(byte[] rData, ushort rRegAddr, ushort rLen, ref DeviceSystemData rSystemData)
        {
            int i, j;
            short reg_count;
            short reg_value;
            i = 2;
            reg_count = (short)(rData[i] / 2); i += 1;

            for (j = 0; j < reg_count; j++)
            {
                reg_value = (short)(rData[i] << 8 | rData[i + 1]);
                SetSP2Register((short)(rRegAddr + j), reg_value, ref rSystemData);
                i += 2;
            }
        }
        private static void ReadSP1RegisterProcess(byte[] rData, ushort rRegAddr, ushort rLen, ref DeviceSystemData rSystemData)
        {
            int i, j;
            short reg_count;
            short reg_value;
            i = 2;
            reg_count = (short)(rData[i] / 2); i += 1;

            for (j = 0; j < reg_count; j++)
            {
                reg_value = (short)(rData[i] << 8 | rData[i + 1]);
                SetSP1Register((short)(rRegAddr + j), reg_value, ref rSystemData);
                i += 2;
            }
        }

        private static readonly int CURRENT_OFFSET = 10000;
        private static readonly int TEMP_OFFSET = 400;

        private static short MakeWarningData_AMG(short reg_value)
        {
            byte[] reg_byte = new byte[2];
            ushort result = 0;

            reg_byte[1] = (byte)(reg_value >> 8);
            reg_byte[0] = (byte)(reg_value >> 0);

            if (((reg_byte[0] >> 0) & 0x01) == 0x01) result |= (ushort)(1 << 4);       // Cell Over Voltage
            if (((reg_byte[0] >> 1) & 0x01) == 0x01) result |= (ushort)(1 << 5);       // Cell Under Voltage
            if (((reg_byte[0] >> 2) & 0x01) == 0x01) result |= (ushort)(1 << 2);       // Pack Over Voltage
            if (((reg_byte[0] >> 3) & 0x01) == 0x01) result |= (ushort)(1 << 3);       // Pack Under Voltage
            if (((reg_byte[0] >> 4) & 0x01) == 0x01) result |= (ushort)(1 << 6);       // Charge Over Current
            if (((reg_byte[0] >> 5) & 0x01) == 0x01) result |= (ushort)(1 << 7);       // Discharge Over Current
            if (((reg_byte[0] >> 6) & 0x01) == 0x01) result |= (ushort)(1 << 14);      // ENV Over Temperature
            if (((reg_byte[0] >> 7) & 0x01) == 0x01) result |= (ushort)(1 << 15);      // ENV Under Temperature

            if (((reg_byte[1] >> 0) & 0x01) == 0x01) result |= (ushort)(1 << 0);       // Chg Over Temperature
            if (((reg_byte[1] >> 1) & 0x01) == 0x01) result |= (ushort)(1 << 1);       // Chg Under Temperature
            if (((reg_byte[1] >> 2) & 0x01) == 0x01) result |= (ushort)(1 << 0);       // Dch Over Temperature
            if (((reg_byte[1] >> 3) & 0x01) == 0x01) result |= (ushort)(1 << 1);       // Dch Under Temperature
            if (((reg_byte[1] >> 4) & 0x01) == 0x01) result |= (ushort)(1 << 11);      // SOC Low Alarm
            if (((reg_byte[1] >> 5) & 0x01) == 0x01) result |= (ushort)(1 << 9);       // Diff Voltage Alarm
            if (((reg_byte[1] >> 6) & 0x01) == 0x01) result |= (ushort)(1 << 13);      // CB On/OFF (1: OFF, 0: ON)
            if (((reg_byte[1] >> 7) & 0x01) == 0x01) result |= (ushort)(1 << 12);      // reserved

            return (short)result;
        }

        private static short MakeProtectData_AMG(short reg_value)
        {
            byte[] reg_byte = new byte[2];
            ushort result = 0;

            reg_byte[1] = (byte)(reg_value >> 8);
            reg_byte[0] = (byte)(reg_value >> 0);

            if (((reg_byte[0] >> 0) & 0x01) == 0x01) result |= (ushort)(1 << 4);        // Cell Over Voltage
            if (((reg_byte[0] >> 1) & 0x01) == 0x01) result |= (ushort)(1 << 2);        // Pack Over Voltage
            if (((reg_byte[0] >> 2) & 0x01) == 0x01) result |= (ushort)(1 << 5);        // Cell Under Voltage
            if (((reg_byte[0] >> 3) & 0x01) == 0x01) result |= (ushort)(1 << 3);        // Pack Under Voltage
            if (((reg_byte[0] >> 4) & 0x01) == 0x01) result |= (ushort)(1 << 6);        // Cha Over Current 1
            if (((reg_byte[0] >> 5) & 0x01) == 0x01) result |= (ushort)(1 << 6);        // Cha Over Current 2
            if (((reg_byte[0] >> 6) & 0x01) == 0x01) result |= (ushort)(1 << 7);        // Dch Over Current 1
            if (((reg_byte[0] >> 7) & 0x01) == 0x01) result |= (ushort)(1 << 7);        // Dch Over Current 2

            if (((reg_byte[1] >> 0) & 0x01) == 0x01) result |= (ushort)(1 << 8);        // SC Protect / CB off
            if (((reg_byte[1] >> 1) & 0x01) == 0x01) result |= (ushort)(1 << 0);        // Chg Over Temperature
            if (((reg_byte[1] >> 2) & 0x01) == 0x01) result |= (ushort)(1 << 1);        // Chg Under Temperature
            if (((reg_byte[1] >> 3) & 0x01) == 0x01) result |= (ushort)(1 << 0);        // Dch Over Temperature
            if (((reg_byte[1] >> 4) & 0x01) == 0x01) result |= (ushort)(1 << 1);        // Dch Under Temperature

            return (short)result;
        }

        private static void MakeAlarm(ref DeviceSystemData rSystemData)
        {
            if (((rSystemData.StatusData.faultstatus >> 14) & 0x0001) == 0x0001)
            {
                rSystemData.StatusData.batteryStatus = 4;       // Anti-theft Gyroscope
            }
            else if (((rSystemData.StatusData.faultstatus >> 15) & 0x0001) == 0x0001)
            {
                rSystemData.StatusData.batteryStatus = 5;       // Anti-theft Comm.
            }
            else if (rSystemData.StatusData.protection != 0x0000)
            {
                rSystemData.StatusData.batteryStatus = 2;
            }
            else if (rSystemData.StatusData.warning != 0x0000)
            {
                rSystemData.StatusData.batteryStatus = 1;
            }
            else
            {
                rSystemData.StatusData.batteryStatus = 0;
            }
        }

        private static void SetHoldingRegister(short reg_addr, short reg_value, ref DeviceSystemData rSystemData)
        {
            switch (reg_addr)
            {
                //100 ~111 : Model_Product Name
                case 100:
                case 101:
                case 102:
                case 103:
                case 104:
                case 105:
                case 106:
                case 107:
                case 108:
                case 109:
                case 110:
                case 111:
                    int mReg = reg_addr - 100;
                    rSystemData.Information.Model_Byte[(mReg * 2) + 0] = (byte)(reg_value >> 8);
                    rSystemData.Information.Model_Byte[(mReg * 2) + 1] = (byte)(reg_value >> 0);
                    if (reg_addr == 111)
                    {
                        rSystemData.Information.ModelName = Encoding.Default.GetString(rSystemData.Information.Model_Byte).Trim('\0');
                    }
                    break;
                //112 ~114 : FW Version
                case 112:
                case 113:
                case 114:
                    int fReg = reg_addr - 112;
                    rSystemData.Information.FwVer_Byte[(fReg * 2) + 0] = (byte)(reg_value >> 8);
                    rSystemData.Information.FwVer_Byte[(fReg * 2) + 1] = (byte)(reg_value >> 0);
                    if (reg_addr == 114)
                    {
                        rSystemData.Information.SwProductRev = Encoding.Default.GetString(rSystemData.Information.FwVer_Byte).Trim('\0');
                    }
                    break;
                //115 ~ 122 : BMS Serial number
                case 115:
                case 116:
                case 117:
                case 118:
                case 119:
                case 120:
                case 121:
                case 122:
                    int snReg = reg_addr - 115;
                    rSystemData.Information.BMS_SN[(snReg * 2) + 0] = (byte)(reg_value >> 8);
                    rSystemData.Information.BMS_SN[(snReg * 2) + 1] = (byte)(reg_value >> 0);
                    if (reg_addr == 122)
                    {
                        rSystemData.Information.HwSerialNumber = Encoding.Default.GetString(rSystemData.Information.BMS_SN).Trim('\0');
                    }
                    break;
                //123 ~ 127 : Manufacturer
                case 123:
                case 124:
                case 125:
                case 126:
                case 127:
                    int mnReg = reg_addr - 123;
                    rSystemData.Information.Vendor_Byte[(mnReg * 2) + 0] = (byte)(reg_value >> 8);
                    rSystemData.Information.Vendor_Byte[(mnReg * 2) + 1] = (byte)(reg_value >> 0);
                    if (reg_addr == 127)
                    {
                        rSystemData.Information.VendorName = Encoding.Default.GetString(rSystemData.Information.Vendor_Byte).Trim('\0');
                    }
                    break;
                default:
                    break;
            }
        }
        private static void SetInputRegister(short reg_addr, short reg_value, ref DeviceSystemData rSystemData)
        {
            switch (reg_addr)
            {
                case 0x0FFF:
                    rSystemData.ValueData.voltageOfPack = (short)(reg_value / 10);
                    rSystemData.active = true;
                    break;       // 48.00V = 4800
                case 0x1000: rSystemData.ValueData.current = (short)(reg_value - CURRENT_OFFSET); break;          // 100.0A = 1000 -10000 = 0;
                case 0x1001: rSystemData.ValueData.remainingCapacity = (short)(reg_value / 1); break;    // 100.0Ah = 1000
                case 0x1002: rSystemData.ValueData.Ext1Temperature = (short)(reg_value - TEMP_OFFSET); break;      // 10.0C = 100
                case 0x1003: rSystemData.ValueData.Ext2Temperature = (short)(reg_value - TEMP_OFFSET); break;      // 10.0C = 100

                case 0x1004: rSystemData.StatusData.warning = MakeWarningData_AMG(reg_value); break;             // Warning Status
                case 0x1005: rSystemData.StatusData.protection = MakeProtectData_AMG(reg_value); break;          // Protection Status
                case 0x1006:    // Fault Status
                    rSystemData.StatusData.faultstatus = (short)(reg_value / 1);
                    rSystemData.StatusData.status = (short)((reg_value >> 8) & 0x0003);
                    MakeAlarm(ref rSystemData);
                    break;

                case 0x1007: rSystemData.ValueData.rSOC = (short)(reg_value / 10); break;                // 100.00% = 10000
                case 0x1008: rSystemData.ValueData.stateOfHealth = (short)(reg_value / 10); break;       // 100.00% = 10000

                case 0x1009: rSystemData.ValueData.fullChargeCapacity = (short)(reg_value / 1); break;   // 100.0Ah = 1000
                case 0x100A: rSystemData.ValueData.cycleCount = (short)(reg_value / 1); break;           // 100 cycles = 100
                case 0x100B: rSystemData.ValueData.NumOfCells = (short)(reg_value / 1); break;           // 15 cells = 15
                case 0x100C:    // Cell Voltage #1
                case 0x100D:    // Cell Voltage #2
                case 0x100E:    // Cell Voltage #3
                case 0x100F:    // Cell Voltage #4
                case 0x1010:    // Cell Voltage #5
                case 0x1011:    // Cell Voltage #6
                case 0x1012:    // Cell Voltage #7
                case 0x1013:    // Cell Voltage #8
                case 0x1014:    // Cell Voltage #9
                case 0x1015:    // Cell Voltage #10
                case 0x1016:    // Cell Voltage #11
                case 0x1017:    // Cell Voltage #12
                case 0x1018:    // Cell Voltage #13
                case 0x1019:    // Cell Voltage #14
                case 0x101A:    // Cell Voltage #15
                case 0x101B:    // Cell Voltage #16
                case 0x101C:    // Cell Voltage #17
                case 0x101D:    // Cell Voltage #18
                case 0x101E:    // Cell Voltage #19
                case 0x101F:    // Cell Voltage #20
                case 0x1020:    // Cell Voltage #21
                case 0x1021:    // Cell Voltage #22
                case 0x1022:    // Cell Voltage #23
                case 0x1023:    // Cell Voltage #24
                case 0x1024:    // Cell Voltage #25
                case 0x1025:    // Cell Voltage #26
                case 0x1026:    // Cell Voltage #27
                case 0x1027:    // Cell Voltage #28
                case 0x1028:    // Cell Voltage #29
                case 0x1029:    // Cell Voltage #30
                case 0x102A:    // Cell Voltage #31
                case 0x102B:    // Cell Voltage #32
                case 0x102C:    // Cell Voltage #33
                case 0x102D:    // Cell Voltage #34
                case 0x102E:    // Cell Voltage #35
                case 0x102F:    // Cell Voltage #36
                case 0x1030:    // Cell Voltage #37
                case 0x1031:    // Cell Voltage #38
                case 0x1032:    // Cell Voltage #39 - 3.256V = 3256
                    int cNo = reg_addr - 0x100C;
                    if (cNo < rSystemData.ValueData.CellVoltage.Length)
                    {
                        rSystemData.ValueData.CellVoltage[cNo] = (ushort)(reg_value / 1);
                    }
                    csUtils.MakeMaxAvgMinCellVoltage(ref rSystemData, 39);
                    break;
                case 0x1033: rSystemData.ValueData.NumOfTemps = (short)(reg_value / 1); break;           // 5 temps = 5
                case 0x1034: rSystemData.ValueData.CellTemperature[0] = (short)(reg_value - TEMP_OFFSET); break;
                case 0x1035: rSystemData.ValueData.CellTemperature[1] = (short)(reg_value - TEMP_OFFSET); break;
                case 0x1036: rSystemData.ValueData.CellTemperature[2] = (short)(reg_value - TEMP_OFFSET); break;
                case 0x1037: rSystemData.ValueData.CellTemperature[3] = (short)(reg_value - TEMP_OFFSET); break;
                case 0x1038: rSystemData.ValueData.CellTemperature[4] = (short)(reg_value - TEMP_OFFSET); break;
                case 0x1039: rSystemData.ValueData.CellTemperature[5] = (short)(reg_value - TEMP_OFFSET); break;
                case 0x103A: rSystemData.ValueData.CellTemperature[6] = (short)(reg_value - TEMP_OFFSET); break;
                case 0x103B:        // 25.3C = 253 - 400 = -147 (Offset -400)
                    rSystemData.ValueData.CellTemperature[7] = (short)(reg_value - TEMP_OFFSET);
                    csUtils.MakeMaxAvgMinTemperature(ref rSystemData, 8);
                    break;
                case 0x103C: rSystemData.MaxValue.MaxChgCurrent = (short)(reg_value); break;      // Max. Charge Current
                case 0x103D: rSystemData.MaxValue.MaxChgTemperature = (short)(reg_value - TEMP_OFFSET); break;      // Max. Charge Temperature
                case 0x103E: rSystemData.MaxValue.MinChgTemperature = (short)(reg_value - TEMP_OFFSET); break;      // Min. Charge Temperature
                case 0x103F: rSystemData.MaxValue.FloatChgVolt = (short)(reg_value / 10); break;      // Float Charge Current
                case 0x1040: rSystemData.MaxValue.BoostChgVolt = (short)(reg_value / 10); break;      // Boost Charge Current

                case 0x3000: rSystemData.CalibrationData.AntiTheftComm.TimeOut = (short)(reg_value / 1); break;
                case 0x3002: rSystemData.CalibrationData.AntiTheftComm.FuncSwitch = (short)(reg_value / 1); break;

                case 0x4000: rSystemData.CalibrationData.AntiTheftGyro.XAxis = (short)(reg_value / 1); break;
                case 0x4001: rSystemData.CalibrationData.AntiTheftGyro.YAxis = (short)(reg_value / 1); break;
                case 0x4002: rSystemData.CalibrationData.AntiTheftGyro.ZAxis = (short)(reg_value / 1); break;
                case 0x4003: rSystemData.CalibrationData.AntiTheftGyro.GyroPolicySel = (short)(reg_value / 1); break;
                case 0x4004: rSystemData.CalibrationData.AntiTheftGyro.GyroFuncSwitch = (short)(reg_value / 1); break;
                case 0x4006: rSystemData.CalibrationData.AntiTheftGyro.GyroState = (short)(reg_value / 1); break;
                default:
                    break;
            }
        }

        private static void SetSP1Register(short reg_addr, short reg_value, ref DeviceSystemData rSystemData)
        {
            switch (reg_addr)
            {
                case 0x4000: rSystemData.GyroValue.X_axis = (short)(reg_value); break;       // 
                case 0x4001: rSystemData.GyroValue.Y_axis = (short)(reg_value); break;       // 
                case 0x4002: rSystemData.GyroValue.Z_axis = (short)(reg_value); break;       // 
                case 0x4003: rSystemData.GyroValue.Gyro_policy_sel = (short)(reg_value); break;          //
                case 0x4004: rSystemData.GyroValue.Gyro_func_sw = (short)(reg_value); break;          //
                case 0x4006: rSystemData.GyroValue.Gyro_state = (short)(reg_value); break;          //
                default:
                    break;
            }
        }
        private static void SetSP2Register(short reg_addr, short reg_value, ref DeviceSystemData rSystemData)
        {
            switch (reg_addr)
            {
                case 0x3000: rSystemData.GyroValue.X_axis = (short)(reg_value); break;       // 
                case 0x3002: rSystemData.GyroValue.Y_axis = (short)(reg_value); break;       // 
                default:
                    break;
            }
        }

        private static void SetCoilRegister(short reg_addr, short reg_value, ref DeviceSystemData rSystemData)
        {
            switch (reg_addr)
            {
                case 0x3078:
                    rSystemData.StatusData.relayStatus = reg_value;
                    rSystemData.CalibrationData.FetCalib.FetStatus = reg_value;
                    break;
                case 0x502E:
                    rSystemData.CalibrationData.ChaMode.Mode = reg_value;
                    break;
            }
        }
    }
}
