
package uk.co.wingpath.modbus;

import java.util.*;

/**
* This class implements the various counters whose values are returned by
* the diagnostics and get-comm-event-counter commands.
* <p>Although the values of all these counters are returned by a
* {@link ModbusSlave}, some are incremented by the ModbusSlave
* (CommExceptionCount, MessageCount, NoResponseCount, CommEventCount) and some
* are incremented by the {@link PacketType} (BusMessageCount, CommErrorCount,
* OverrunCount).
* <p>These counters are intended for serial devices, so only the
* {@link RtuPacketType} and {@link AsciiPacketType} increment the counters.
* ModbusSlave always increments the relevant counters, since it does not
* know what kind of interface is being used.
* <p>Comments in quotes are taken from Appendix A of 'Modbus over Serial Line -
* Specification & implementation guide V1.02'.
*/

public class ModbusCounters
{
    private int busMessageCount = 0;
    private int commErrorCount = 0;
    private int commExceptionCount = 0;
    private int messageCount = 0;
    private int noResponseCount = 0;
    private int overrunCount = 0;
    private int commEventCount = 0;
    private LinkedList<Byte> commEventLog = new LinkedList<Byte> ();

    /**
    * Clears the counters (diagnostics sub-function 0A).
    */
    public void reset ()
    {
        busMessageCount = 0;
        commErrorCount = 0;
        commExceptionCount = 0;
        messageCount = 0;
        noResponseCount = 0;
        overrunCount = 0;
        commEventCount = 0;
    }

    /**
    * Gets the bus message count (diagnostics sub-function 0B).
    * <p>"Quantity of messages that the remote device has detected on the
    * communications system since its last restart, clear counters operation,
    * or power–up. Messages with bad CRC are not taken into account."
    * @return the bus message count.
    */
    public int getBusMessageCount ()
    {
        return busMessageCount;
    }

    /**
    * Increments the bus message count.
    */
    public void incBusMessageCount ()
    {
        ++busMessageCount;
    }
    
    /**
    * Gets the bus communication error count (diagnostics sub-function 0C).
    * <p>"Quantity of CRC errors encountered by the remote device since its last
    * restart, clear counters operation, or power–up. In case of an error
    * detected on the character level, (overrun, parity error), or in case of a
    * message length < 3 bytes, the receiving device is not able to calculate
    * the CRC. In such cases, this counter is also incremented."
    * @return the bus communication error count.
    */
    public int getCommErrorCount ()
    {
        return commErrorCount;
    }

    /**
    * Increments the bus communication error count.
    */
    public void incCommErrorCount ()
    {
        ++commErrorCount;
    }
    
    /**
    * Gets the bus exception error count (diagnostics sub-function 0D).
    * <p>"Quantity of MODBUS exception error detected by the remote device
    * since its last restart, clear counters operation, or power–up. It
    * comprises also the error detected in broadcast messages even if an
    * exception message is not returned in this case."
    * @return the bus exception error count.
    */
    public int getCommExceptionCount ()
    {
        return commExceptionCount;
    }

    /**
    * Increments the bus exception error count.
    */
    public void incCommExceptionCount ()
    {
        ++commExceptionCount;
    }
    
    /**
    * Gets the server message count (diagnostics sub-function 0E).
    * <p>"Quantity of messages addressed to the remote device, including
    * broadcast messages, that the remote device has processed since its
    * last restart, clear counters operation, or power–up."
    * @return the server message count.
    */
    public int getMessageCount ()
    {
        return messageCount;
    }

    /**
    * Increments the server message count.
    */
    public void incMessageCount ()
    {
        ++messageCount;
    }
    
    /**
    * Gets the server no response count (diagnostics sub-function 0F).
    * <p>"Quantity of messages received by the remote device for which it
    * returned no response (neither a normal response nor an exception
    * response), since its last restart, clear counters operation, or power–up.
    * Then, this counter counts the number of broadcast messages it has
    * received."
    * @return the server no response count.
    */
    public int getNoResponseCount ()
    {
        return noResponseCount;
    }

    /**
    * Increments the server no response count.
    */
    public void incNoResponseCount ()
    {
        ++noResponseCount;
    }
    
    /**
    * Gets the bus overrun count (diagnostics sub-function 12).
    * <p>"Quantity of messages addressed to the remote device that it could not
    * handle due to a character overrun condition, since its last restart, clear
    * counters operation, or power–up. A character overrun is caused by data
    * characters arriving at the port faster than they can be stored, or by the
    * loss of a character due to a hardware malfunction."
    * @return the bus overrun count.
    */
    public int getOverrunCount ()
    {
        return overrunCount;
    }

    /**
    * Increments the bus overrun count.
    */
    public void incOverrunCount ()
    {
        ++overrunCount;
    }
    
    /**
    * Clears the bus overrun count (diagnostics sub-function 14).
    */
    public void clearOverrunCount ()
    {
        overrunCount = 0;
    }
    
    /**
    * Gets the comm event count (function 0B).
    * <p>"The device’s event counter is incremented once for each successful
    * message completion. It is not incremented for exception responses,
    * poll commands, or fetch event counter commands."
    * @return the comm event count.
    */
    public int getCommEventCount ()
    {
        return commEventCount;
    }

    /**
    * Increments the comm event count.
    */
    public void incCommEventCount ()
    {
        ++commEventCount;
    }
    
    /**
    * Decrements the comm event count.
    */
    public void decCommEventCount ()
    {
        --commEventCount;
    }

    /**
    * Gets the "Comm Event Log".
    * @return the Comm Event Log.
    */
    public byte [] getCommEventLog ()
    {
        byte [] log = new byte [commEventLog.size ()];

        for (int i = 0 ; i < log.length ; ++i)
            log [i] = commEventLog.get (i);

        return log;
    }

    /**
    * Clears the "Comm Event Log".
    */
    public void clearCommEventLog ()
    {
        commEventLog = new LinkedList<Byte> ();
    }

    /**
    * Adds an "event" to the "Comm Event Log".
    * @param event the event to be added.
    */
    public void addEvent (int event)
    {
        commEventLog.addFirst ((byte) event);
        if (commEventLog.size () > 64)
            commEventLog.removeLast ();
    }
}

