package com.xixun.communicate.Protocol;

import com.xixun.communicate.Define;
import com.xixun.communicate.IFunctionCallBack;
import com.xixun.display.MyLog;

/**
 * Created by Administrator on 2018/06/08.
 */

public class Modbus {
    public static int MAX_PACKET_SIZE = (0x20);
    public static final byte MODBUS_COMMAND_CARRIAGE_NUMBER = (0x06);
    public static final byte MODBUS_COMMAND_TRAIN_NUMBER = (0x10);


    public byte ucCommandType = 0;
    public int iReceLength = 0;
    public int iRegAddress=0;
    public byte[] ReceBuffer = new byte[MAX_PACKET_SIZE];

    int uiCrcCheckCode = 0;
    int iReceCounter = 0;

    FRAME_PROTCAL_STAGE frameProtocalStage = FRAME_PROTCAL_STAGE.HEAD;

    byte ucDeviceAddress=0;

    IFunctionCallBack FunctionCallBack=null;
    public Modbus(byte ucDeviceAddress,IFunctionCallBack FunctionCallBack)
    {   this.ucDeviceAddress=ucDeviceAddress;
        this.FunctionCallBack = FunctionCallBack;
    }

    public void ReceFrame(byte ucReceData)
    {
        ReceBuffer[iReceCounter++] = (frameProtocalStage==FRAME_PROTCAL_STAGE.CHECK)? 0 : ucReceData;

        ReceBuffer[iReceCounter] = 0; // 补零，用作字符串结束符。

        //System.out.println("frameProtocalStage= "+frameProtocalStage  + " Data: "+ Long.toHexString(ucReceData).toUpperCase());

        switch (frameProtocalStage)
        {
            case HEAD:
                if (ReceHead(ucReceData)) frameProtocalStage = FRAME_PROTCAL_STAGE.NAMEPALAT;
                break;
            case NAMEPALAT:
                if (ReceNamepalat(ucReceData)) frameProtocalStage = (iReceLength > 0) ? FRAME_PROTCAL_STAGE.TEXT : FRAME_PROTCAL_STAGE.CHECK;
                break;
            case TEXT:
                if (ReceText(ucReceData)) frameProtocalStage = FRAME_PROTCAL_STAGE.CHECK;
                break;
            case CHECK:
                if (ReceCheck(ucReceData))
                    if (FunctionCallBack != null) {
                        FunctionCallBack.LastReceDataProcess(ReceBuffer, iReceLength);
                        MyLog.i("-----------ReceBuffer2");
                    }
                break;
            default:
                ReceClear();
                break;
        }
    }

    Boolean ReceHead(byte ucReceData)
    {
        switch (iReceCounter)
        {
            case 1:
                if (ucReceData != ucDeviceAddress) ReceClear();
                else
                {
                    uiCrcCheckCode=0;
                    uiCrcCheckCode= Define.Crc16_Add(uiCrcCheckCode,ucReceData);

                    iReceCounter=0;
                    return true;
                }
                break;

            default:
                ReceClear();
                break;
        }
        return false;
    }

    Boolean ReceNamepalat(byte ucReceData)
    {   uiCrcCheckCode=Define.Crc16_Add(uiCrcCheckCode,ucReceData);
        //System.out.println("Received iReceCounter: " + iReceCounter);
        switch(iReceCounter)
        {
            case 1:
                ucCommandType=ucReceData;
                switch (ucCommandType)
                {
                    case MODBUS_COMMAND_CARRIAGE_NUMBER:
                        iReceLength=2;
                        break;
                    case MODBUS_COMMAND_TRAIN_NUMBER:
                        iReceLength=6;
                        break;
                    default:
                        ReceClear();
                        break;
                }
                //System.out.println("Received Command: " + ucCommandType);
                break;
            case 2:
                iRegAddress=((int)ucReceData)<<8;
                break;
            case 3:
                iRegAddress|=ucReceData;
                if (iRegAddress>1024) ReceClear();
                else {
                    //System.out.println("Received iRegAddress: " + iRegAddress);
                    iReceCounter=0;
                    return true;
                }
                break;
            default:
                ReceClear();
                break;
        }
        return false;
    }

    Boolean ReceText(byte ucReceData)
    {   uiCrcCheckCode=Define.Crc16_Add(uiCrcCheckCode,ucReceData);
        if (iReceCounter == iReceLength)
        {   //iReceCounter=0; // 会清掉数据。
            MyLog.i(" uiCrcCheckCode: 0x" + Long.toHexString(uiCrcCheckCode).toUpperCase());
            return true;
        }
        return false;
    }

    Boolean ReceCheck(byte ucReceData)
    {   // 兼容校验码高低字节，均可先后传。
        switch(iReceCounter-iReceLength)
        {
            case 1:
                if ((uiCrcCheckCode%0x100)!=(ucReceData&0xFF)) { // 先传低字节校验码。
                    MyLog.i(" (uiCrcCheckCode%0x100):  0x" + Long.toHexString((uiCrcCheckCode%0x100)).toUpperCase());
                    MyLog.i(" ucReceData:  0x" + Long.toHexString(((int)ucReceData)).toUpperCase());

                    if (uiCrcCheckCode/0x100!=(ucReceData&0xFF)) { // 先传高字节校验码。
                        MyLog.i(" (uiCrcCheckCode/0x100):  0x" + Long.toHexString((uiCrcCheckCode / 0x100)).toUpperCase());
                        MyLog.i(" ucReceData:  0x" + Long.toHexString((ucReceData)).toUpperCase());
//忽略校验码                        ReceClear();
                    }
                }
                break;
            case 2:
                ReceClear();
                if (uiCrcCheckCode/0x100!=(ucReceData&0xFF)) { // 再传高字节校验码。
                    MyLog.i(" (uiCrcCheckCode/0x100):  0x" + Long.toHexString((uiCrcCheckCode / 0x100)).toUpperCase());
                    MyLog.i(" ucReceData:  0x" + Long.toHexString((ucReceData)).toUpperCase());

                    if ((uiCrcCheckCode%0x100)!=(ucReceData&0xFF)) { // 再传低字节校验码。
                        MyLog.i(" (uiCrcCheckCode%0x100):  0x" + Long.toHexString((uiCrcCheckCode%0x100)).toUpperCase());
                        MyLog.i(" ucReceData:  0x" + Long.toHexString(((int)ucReceData)).toUpperCase());
//忽略校验码                        return false;
                    }
                }
                return true;
            default:
                ReceClear();
                break;
        }
        return false;
    }


    void ReceClear()
    {
        frameProtocalStage = FRAME_PROTCAL_STAGE.HEAD;
        iReceCounter=0;
    }

    public void TransData_Byte(byte ucAddress, byte ucCommand, int uiRegAddress,byte[] DataBuffer, int iLength)
    {
        int iOffset = 0;
        byte[] dataBuffer = new byte[iLength + 6];

        dataBuffer[iOffset++] = ucAddress;
        dataBuffer[iOffset++] = ucCommand;
        dataBuffer[iOffset++] = (byte)(uiRegAddress>>8);                        // 寄存器地址高。
        dataBuffer[iOffset++] = (byte)(uiRegAddress);                           // 寄存器地址低。

        if (DataBuffer!=null && iLength>0) {
            System.arraycopy(DataBuffer, 0, dataBuffer, iOffset, iLength);    // 正文内容。
            iOffset += iLength;
        }

        int uiCrc=Define.chksum_crc16(dataBuffer,0,iOffset);

        dataBuffer[iOffset++] =  (byte)(uiCrc);                                 // CRC16低字节。
        dataBuffer[iOffset++] =  (byte)(uiCrc>>8);                              // CRC16高字节。

        if (FunctionCallBack!=null) FunctionCallBack.LastSendDataProcess(dataBuffer, iOffset);
    }

}
