
package uk.co.wingpath.modbus;


/**
* This class is used to represent Modbus response messages.
*/
public class ModbusResponse
    extends ModbusMessage
{
    /**
    * Constructs a ModbusResponse with the specified slave id,
    * function code, transaction identifier, and data.
    * @param slaveId slave identifier.
    * @param function function code.
    * @param transId transaction identifier, or -1 if the transaction
    * identifier is not defined (i.e. for RTU and ASCII packets).
    * @param data body of message.
    */
    public ModbusResponse (int slaveId, int function, int transId, byte [] data)
    {
        super (slaveId, function, transId, data);
    }

    /**
    * Checks that the response size is what is expected, and throws a
    * ModbusException if isn't.
    * @param size expected response size.
    * @throws ModbusException if the response size is not correct.
    */
    public void checkSize (int size)
        throws ModbusException
    {
        if (size () != size)
        {
            Modbus.dataError ("S201", "Response PDU size incorrect: " +
                (size () + 1) + " instead of " + (size + 1));
        }
    }

    /**
    * Checks that the response size is not less than the specified minimum,
    * and throws a ModbusException if is.
    * @param minSize expected minimum response size.
    * @throws ModbusException if the response size is too small.
    */
    public void checkMinSize (int minSize)
        throws ModbusException
    {
        if (size () < minSize)
        {
            Modbus.dataError ("S202", "Response PDU too short: " +
                (size () + 1) + " when it should be at least " + (minSize + 1));
        }
    }

    @Override
    public void checkSize (boolean allowLongMessages)
        throws ModbusException
    {
        switch (function)
        {
        case Modbus.FUNC_READ_HOLDING_REGISTERS:
        case Modbus.FUNC_READ_INPUT_REGISTERS:
        case Modbus.FUNC_READ_COILS:
        case Modbus.FUNC_READ_DISCRETE_INPUTS:
        case Modbus.FUNC_READ_WRITE_MULTIPLE_REGISTERS:
        case Modbus.FUNC_REPORT_SLAVE_ID:
            checkMinSize (1);
            if (!allowLongMessages)
                checkSize (getByte (0) + 1);
            break;

        case Modbus.FUNC_WRITE_MULTIPLE_REGISTERS:
            checkSize (4);
            break;

        case Modbus.FUNC_WRITE_SINGLE_REGISTER:
            checkMinSize (3);
            break;

        case Modbus.FUNC_MASK_WRITE_REGISTER:
            checkMinSize (4);
            break;

        case Modbus.FUNC_WRITE_MULTIPLE_COILS:
            checkSize (4);
            break;

        case Modbus.FUNC_WRITE_SINGLE_COIL:
            checkSize (4);
            break;

        case Modbus.FUNC_READ_EXCEPTION_STATUS:
            checkSize (1);
            break;

        case Modbus.FUNC_DIAGNOSTICS:
            checkMinSize (2);
            break;

        case Modbus.FUNC_GET_COMM_EVENT_COUNTER:
            checkSize (4);
            break;

        case Modbus.FUNC_ENCAPSULATED_INTERFACE_TRANSPORT:
            checkMinSize (1);
            if (getByte (0) == Modbus.MEI_READ_DEVICE_IDENTIFICATION)
                checkMinSize (6);
        }
    }

    @Override
    void traceExplanation (Tracer tracer)
    {
        if (tracer == null || !tracer.isTraceEnabled ())
            return;
        StringBuilder msg = new StringBuilder ();
        msg.append ("Response: ");
        msg.append (getFunctionName ());

        try
        {
            switch (function)
            {
            case Modbus.FUNC_READ_HOLDING_REGISTERS:
            case Modbus.FUNC_READ_INPUT_REGISTERS:
            case Modbus.FUNC_READ_COILS:
            case Modbus.FUNC_READ_DISCRETE_INPUTS:
            case Modbus.FUNC_READ_WRITE_MULTIPLE_REGISTERS:
                msg.append (": byte count ");
                msg.append (getByte (0));
                msg.append (", data bytes ");
                msg.append (data.length - 1);
                break;

            case Modbus.FUNC_WRITE_MULTIPLE_REGISTERS:
            case Modbus.FUNC_WRITE_MULTIPLE_COILS:
                msg.append (": address ");
                msg.append (getInt (0));
                msg.append (", count ");
                msg.append (getInt (2));
                break;

            case Modbus.FUNC_WRITE_SINGLE_REGISTER:
                msg.append (": address ");
                msg.append (getInt (0));
                msg.append (", data bytes ");
                msg.append (data.length - 2);
                break;

            case Modbus.FUNC_MASK_WRITE_REGISTER:
                msg.append (": address ");
                msg.append (getInt (0));
                msg.append (", data bytes ");
                msg.append (data.length - 2);
                break;

            case Modbus.FUNC_WRITE_SINGLE_COIL:
                msg.append (": address ");
                msg.append (getInt (0));
                msg.append (", value ");
                msg.append (Integer.toHexString (getInt (2)));
                break;

            case Modbus.FUNC_READ_EXCEPTION_STATUS:
                msg.append (": status ");
                msg.append (Integer.toHexString (getByte (0)));
                break;

            case Modbus.FUNC_DIAGNOSTICS:
                msg.append (": data ");
                msg.append (getInt (2));
                break;

            case Modbus.FUNC_GET_COMM_EVENT_COUNTER:
                msg.append (": status ");
                msg.append (getInt (0));
                msg.append (", count ");
                msg.append (getInt (2));
                break;

            case Modbus.FUNC_REPORT_SLAVE_ID:
                msg.append (": byte count ");
                msg.append (getByte (0));
                break;

            case Modbus.FUNC_ENCAPSULATED_INTERFACE_TRANSPORT:
                if (getByte (0) == Modbus.MEI_READ_DEVICE_IDENTIFICATION)
                {
                    msg.append (": code ");
                    msg.append (getByte (1));
                }
                break;

            default:
                break;
            }
        }
        catch (Exception e)
        {
        }

        traceExplanation (tracer, null, msg.toString ());
    }
}

