Package modbuspal.slave

Source Code of modbuspal.slave.ModbusRegisters$RegisterCopy

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

package modbuspal.slave;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Set;
import javax.swing.JPanel;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableModel;
import modbuspal.binding.Binding;
import modbuspal.link.ModbusSlaveProcessor;
import modbuspal.main.ModbusConst;
import static modbuspal.main.ModbusConst.FC_READ_HOLDING_REGISTERS;
import static modbuspal.main.ModbusConst.FC_READ_WRITE_MULTIPLE_REGISTERS;
import static modbuspal.main.ModbusConst.FC_WRITE_MULTIPLE_REGISTERS;
import static modbuspal.main.ModbusConst.FC_WRITE_SINGLE_REGISTER;
import static modbuspal.main.ModbusConst.XC_ILLEGAL_DATA_ADDRESS;
import static modbuspal.main.ModbusConst.XC_ILLEGAL_DATA_VALUE;
import static modbuspal.main.ModbusConst.XC_SUCCESSFUL;
import modbuspal.main.ModbusValuesMap;
import modbuspal.main.ModbusPalXML;
import modbuspal.master.ModbusMasterRequest;
import modbuspal.toolkit.ModbusTools;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
* Storage for the holding registers of a modbus slave
* @author nnovic
*/
public class ModbusRegisters
implements ModbusPduProcessor, TableModel, ModbusPalXML, ModbusConst
{

    class RegisterCopy
    {
        int registerAddress;
        Integer registerValue;
        String registerName;
        Binding registerBinding;
        RegisterCopy(int address, Integer value, String name, Binding b)
        {
            registerAddress = address;
            registerValue = value;
            registerName = name;
            registerBinding = b;
        }
    }

    /** name of the column displaying the address of the registers in the TableModel. */
    public static final String ADDRESS_COLUMN_NAME = "Address";
   
    /** index of the column displaying the address of the registers in the TableModel. */
    public static final int ADDRESS_COLUMN_INDEX = 0;
   
    /** index of the column displaying the value of the registers in the TableModel. */
    public static final int VALUE_COLUMN_INDEX = 1;
   
    /** index of the column displaying the name of the registers in the TableModel. */
    public static final int NAME_COLUMN_INDEX = 2;
   
    /** index of the column displaying the binding of the registers in the TableModel. */
    public static final int BINDING_COLUMN_INDEX = 3;

    /** defines how should be called a single value from the TableModel. */
    protected String TXT_REGISTER = "register";
   
    /** defines the plural form of the TXT_REGISTER word. */
    protected String TXT_REGISTERS = "registers";

    private ModbusValuesMap values = new ModbusValuesMap();
    private HashMap<Integer,String> names = new HashMap<Integer,String>(65536);
    private ArrayList<TableModelListener> tableModelListeners = new ArrayList<TableModelListener>();
    private HashMap<Integer,Binding> bindings = new HashMap<Integer,Binding>(65536);
    private int addressOffset = 1;

    //==========================================================================
    //
    // PDU PROCESSOR
    //
    //==========================================================================

    @Override
    public int processPDU(byte functionCode, ModbusSlaveAddress slaveID, byte[] buffer, int offset, boolean createIfNotExist)
    {
        switch( functionCode )
        {
            case FC_READ_HOLDING_REGISTERS: return processReadMultipleRegistersRequest(functionCode, buffer, offset, createIfNotExist);
            case FC_WRITE_SINGLE_REGISTER: return processWriteSingleRegisterRequest(functionCode, buffer, offset, createIfNotExist);
            case FC_WRITE_MULTIPLE_REGISTERS: return processWriteMultipleRegistersRequest(functionCode, buffer, offset, createIfNotExist);
            case FC_READ_WRITE_MULTIPLE_REGISTERS: return processReadWriteMultipleRegistersRequest(functionCode, buffer, offset, createIfNotExist);
        }
        return -1;
    }

    @Override
    public int buildPDU(ModbusMasterRequest req, ModbusSlaveAddress slaveID, byte[] buffer, int offset, boolean createIfNotExist)
    {
        switch( req.getFunctionCode() )
        {
            case FC_READ_HOLDING_REGISTERS: return buildReadMultipleRegistersRequest(req, buffer, offset, createIfNotExist);
            case FC_WRITE_SINGLE_REGISTER: return buildWriteSingleRegisterRequest(req, buffer, offset, createIfNotExist);
            case FC_WRITE_MULTIPLE_REGISTERS: return buildWriteMultipleRegistersRequest(req, buffer, offset, createIfNotExist);
            case FC_READ_WRITE_MULTIPLE_REGISTERS: return buildReadWriteMultipleRegistersRequest(req, buffer, offset, createIfNotExist);
        }
        return -1;
    }
   
    @Override
    public boolean processPDU(ModbusMasterRequest req, ModbusSlaveAddress slaveID, byte[] buffer, int offset, boolean createIfNotExist)
    {
        switch(req.getFunctionCode() )
        {
            case FC_READ_HOLDING_REGISTERS: return readMultipleRegistersReply(req, buffer, offset, createIfNotExist);
            case FC_WRITE_SINGLE_REGISTER: return writeSingleRegisterReply(req, buffer, offset, createIfNotExist);
            case FC_WRITE_MULTIPLE_REGISTERS: return writeMultipleRegistersReply(req, buffer, offset, createIfNotExist);
            case FC_READ_WRITE_MULTIPLE_REGISTERS: return readWriteMultipleRegistersReply(req, buffer, offset, createIfNotExist);
        }
        return false;
    }
   
   
    @Override
    public JPanel getPduPane()
    {
        return null;
    }

    @Override
    public String getClassName()
    {
        return getClass().getSimpleName();
    }

    @Override
    public ModbusPduProcessor newInstance()
    throws InstantiationException, IllegalAccessException
    {
        return getClass().newInstance();
    }


    @Override
    public void init()
    {
    }

    @Override
    public void reset()
    {
    }


    private int processReadWriteMultipleRegistersRequest(byte functionCode, byte[] buffer, int offset, boolean createIfNotExist)
    {
        int readStartingAddress = ModbusTools.getUint16(buffer, offset+1);
        int quantityToRead = ModbusTools.getUint16(buffer, offset+3);
        int writeStartingAddress = ModbusTools.getUint16(buffer, offset+5);
        int quantityToWrite = ModbusTools.getUint16(buffer, offset+7);
        int writeByteCount = ModbusTools.getUint8(buffer, offset+9);

        // verify quantity to read
        if( (quantityToRead<1) || (quantityToRead>118) )
        {
            return ModbusSlaveProcessor.makeExceptionResponse(functionCode, XC_ILLEGAL_DATA_ADDRESS, buffer, offset);
        }

        // verify quantity to write
        if( (quantityToWrite<1) || (quantityToWrite>118) || (writeByteCount!=2*quantityToWrite) )
        {
            return ModbusSlaveProcessor.makeExceptionResponse(functionCode, XC_ILLEGAL_DATA_ADDRESS, buffer, offset);
        }

        // verify that registers to be read exist:
        if( exist(readStartingAddress,quantityToRead,createIfNotExist) == false )
        {
            return ModbusSlaveProcessor.makeExceptionResponse(functionCode, XC_ILLEGAL_DATA_VALUE, buffer, offset);
        }

        // verify that registers to be written exist:
        if( exist(writeStartingAddress,quantityToWrite,createIfNotExist) == false )
        {
            return ModbusSlaveProcessor.makeExceptionResponse(functionCode, XC_ILLEGAL_DATA_VALUE, buffer, offset);
        }

        byte rc = setValues(writeStartingAddress, quantityToWrite, buffer, offset+10);
        if( rc != XC_SUCCESSFUL )
        {
            return ModbusSlaveProcessor.makeExceptionResponse(functionCode, rc, buffer, offset);
        }

        // then perform read operation:
        buffer[offset+1] = (byte) (2*quantityToRead);
        for(int i=0; i<quantityToRead; i++)
        {
            Integer reg = getValue(readStartingAddress+i);
            ModbusTools.setUint16(buffer, offset+2+(2*i), reg);
        }

        return 2 + (2*quantityToRead);
    }


   
    private int buildReadWriteMultipleRegistersRequest(ModbusMasterRequest req, byte[] buffer, int offset, boolean createIfNotExist)
    {
        int readStartingAddress = req.getReadAddress();
        int quantityToRead = req.getReadQuantity();
        int writeStartingAddress = req.getWriteAddress();
        int quantityToWrite = req.getWriteQuantity();
        int writeByteCount = quantityToWrite*2;

        // verify quantity to read
        if( (quantityToRead<1) || (quantityToRead>118) )
        {
            return -1;
        }

        // verify quantity to write
        if( (quantityToWrite<1) || (quantityToWrite>118) || (writeByteCount!=2*quantityToWrite) )
        {
            return -1;
        }

        // verify that registers to be read exist:
        if( exist(readStartingAddress,quantityToRead,createIfNotExist) == false )
        {
            return -1;
        }

        // verify that registers to be written exist:
        if( exist(writeStartingAddress,quantityToWrite,createIfNotExist) == false )
        {
            return -1;
        }

        buffer[offset+0] = req.getFunctionCode();
        ModbusTools.setUint16(buffer, offset+1, readStartingAddress);
        ModbusTools.setUint16(buffer, offset+3, quantityToRead);
        ModbusTools.setUint16(buffer, offset+5, writeStartingAddress);
        ModbusTools.setUint16(buffer, offset+7, quantityToWrite);
        ModbusTools.setUint8(buffer, offset+9, writeByteCount);
       
        byte rc = getValues(writeStartingAddress, quantityToWrite, buffer, offset+10);
        if( rc != XC_SUCCESSFUL )
        {
            return -1;
        }
       
        return 10+writeByteCount;
    }
       
       
    private boolean readWriteMultipleRegistersReply(ModbusMasterRequest req, byte[] buffer, int offset, boolean createIfNotExist)
    {
        int readStartingAddress = req.getReadAddress();
        int quantityToRead = req.getReadQuantity();
        int byteCount = ModbusTools.getUint8(buffer, offset+1);
       
        // verify quantity to read
        if( byteCount != 2*quantityToRead )
        {
            return false;
        }

        // verify that registers to be read exist:
        if( exist(readStartingAddress,quantityToRead,createIfNotExist) == false )
        {
            return false;
        }

        byte rc = setValues(readStartingAddress, quantityToRead, buffer, offset+2);
        if( rc != XC_SUCCESSFUL )
        {
            return false;
        }

        /*// then perform read operation:
        buffer[offset+1] = (byte) (2*quantityToRead);
        for(int i=0; i<quantityToRead; i++)
        {
            Integer reg = getValue(readStartingAddress+i);
            ModbusTools.setUint16(buffer, offset+2+(2*i), reg);
        }*/

        return true;
    }
   
   
   
   
   
    private int processWriteMultipleRegistersRequest(byte functionCode, byte[] buffer, int offset, boolean createIfNotExist)
    {
        int startingAddress = ModbusTools.getUint16(buffer, offset+1);
        int quantity = ModbusTools.getUint16(buffer, offset+3);
        int byteCount = ModbusTools.getUint8(buffer, offset+5);

        if( (quantity<1) || (quantity>123) || (byteCount!=2*quantity) )
        {
            return ModbusSlaveProcessor.makeExceptionResponse(functionCode, XC_ILLEGAL_DATA_VALUE, buffer, offset);
        }

        if( exist(startingAddress,quantity, createIfNotExist) == false )
        {
            return ModbusSlaveProcessor.makeExceptionResponse(functionCode, XC_ILLEGAL_DATA_ADDRESS, buffer, offset);
        }

        byte rc = setValues(startingAddress, quantity, buffer, offset+6);
        if( rc != XC_SUCCESSFUL )
        {
            return ModbusSlaveProcessor.makeExceptionResponse(functionCode, rc, buffer, offset);
        }

        return 5;
    }


    private int buildWriteMultipleRegistersRequest(ModbusMasterRequest req, byte[] buffer, int offset, boolean createIfNotExist)
    {
        int startingAddress = req.getWriteAddress();
        int quantity = req.getWriteQuantity();
        int byteCount = quantity * 2;

        if( (quantity<1) || (quantity>123) )
        {
            return -1;
        }

        if( exist(startingAddress,quantity, createIfNotExist) == false )
        {
            return -1;
        }
       
        buffer[offset+0] = req.getFunctionCode();
        ModbusTools.setUint16(buffer, offset+1, startingAddress);
        ModbusTools.setUint16(buffer, offset+3, quantity);
        ModbusTools.setUint8(buffer, offset+5, byteCount);
       
        byte rc = getValues(startingAddress, quantity, buffer, offset+6);
        if( rc != XC_SUCCESSFUL )
        {
            return -1;
        }

        return 6+byteCount;
    }
   
    private boolean writeMultipleRegistersReply(ModbusMasterRequest req, byte[] buffer, int offset, boolean createIfNotExist)
    {
        int startingAddress = ModbusTools.getUint16(buffer, offset+1);
        int quantity = ModbusTools.getUint16(buffer, offset+3);
       
        if( startingAddress != req.getWriteAddress() )
        {
            return false;
        }

        if( quantity != req.getWriteQuantity() )
        {
            return false;
        }
               
        if( exist(startingAddress,quantity, createIfNotExist) == false )
        {
            return false;
        }

        /*byte rc = setValues(startingAddress, quantity, buffer, offset+6);
        if( rc != XC_SUCCESSFUL )
        {
            return ModbusSlaveProcessor.makeExceptionResponse(functionCode, rc, buffer, offset);
        }

        return 5;*/
        return true;
    }

   
   
    private int processWriteSingleRegisterRequest(byte functionCode, byte[] buffer, int offset, boolean createIfNotExist)
    {
        int address = ModbusTools.getUint16(buffer, offset+1);

        if( exist(address, 1, createIfNotExist) == false )
        {
            return ModbusSlaveProcessor.makeExceptionResponse(functionCode, XC_ILLEGAL_DATA_ADDRESS, buffer, offset);
        }

        Integer reg = ModbusTools.getUint16(buffer, offset + 3);
        byte rc = setValue(address,reg);
        if( rc != XC_SUCCESSFUL )
        {
            return ModbusSlaveProcessor.makeExceptionResponse(functionCode, rc, buffer, offset);
        }

        return 5;
    }


   
    private int buildWriteSingleRegisterRequest(ModbusMasterRequest req, byte[] buffer, int offset, boolean createIfNotExist)
    {
        int address = req.getWriteAddress();

        if( exist(address, 1, createIfNotExist) == false )
        {
            return -1;
        }

        int value = getValue(address);
       
        buffer[offset+0] = req.getFunctionCode();
        ModbusTools.setUint16(buffer, offset+1, address);
        ModbusTools.setUint16(buffer, offset+3, value);

        return 5;
    }
   
    private boolean writeSingleRegisterReply(ModbusMasterRequest req, byte[] buffer, int offset, boolean createIfNotExist)
    {
        int address = req.getWriteAddress();

        if( exist(address, 1, createIfNotExist) == false )
        {
            return false;
        }

        /*Integer reg = ModbusTools.getUint16(buffer, offset + 3);
        byte rc = setValue(address,reg);
        if( rc != XC_SUCCESSFUL )
        {
            return ModbusSlaveProcessor.makeExceptionResponse(functionCode, rc, buffer, offset);
        }

        return 5;*/
        return true;
    }

   
   
   
   
    private int processReadMultipleRegistersRequest(byte functionCode, byte[] buffer, int offset, boolean createIfNotExist)
    {
        int startingAddress = ModbusTools.getUint16(buffer, offset+1);
        int quantity = ModbusTools.getUint16(buffer, offset+3);

        if( (quantity<1) || (quantity>125) )
        {
            return ModbusSlaveProcessor.makeExceptionResponse(functionCode, XC_ILLEGAL_DATA_VALUE, buffer, offset);
        }

        if( exist(startingAddress,quantity,createIfNotExist) == false )
        {
            return ModbusSlaveProcessor.makeExceptionResponse(functionCode, XC_ILLEGAL_DATA_ADDRESS, buffer, offset);
        }

        // write byte count
        buffer[offset+1] = (byte) (2*quantity);

        // write registers
        for(int i=0; i<quantity; i++)
        {
            Integer reg = getValue(startingAddress+i);
            ModbusTools.setUint16(buffer, offset+2+(2*i), reg);
        }

        return 2 + (2*quantity);
    }
   
   
    private int buildReadMultipleRegistersRequest(ModbusMasterRequest req, byte[] buffer, int offset, boolean createIfNotExist)
    {
        ModbusTools.setUint8(buffer, offset+0, req.getFunctionCode()); // function code
        ModbusTools.setUint16(buffer, offset+1, req.getReadAddress()); // starting address
        ModbusTools.setUint16(buffer, offset+3, req.getReadQuantity()); // quantity of registers
        return 5;
    }



    private boolean readMultipleRegistersReply(ModbusMasterRequest req, byte[] buffer, int offset, boolean createIfNotExist)
    {
        int startingAddress = req.getReadAddress();
        int quantity = req.getReadQuantity();
       
        int byteCount = ModbusTools.getUint8(buffer, offset+1);
        if( byteCount != 2*quantity )
        {
            return false;
        }

        if( exist(startingAddress,quantity,createIfNotExist) == false )
        {
            return false;
        }

        // write byte count
        buffer[offset+1] = (byte) (2*quantity);

        // read registers from buffer
        for(int i=0; i<quantity; i++)
        {
            int reg = ModbusTools.getUint16(buffer, offset+2+(2*i) );
            setValue(startingAddress+i, reg);           
        }

        return true;
    }
   
   
   
    //==========================================================================
    //
    // REGISTERS
    //
    //==========================================================================


    /**
     * Creates and initialize several registers with default value.
     * @param startingIndex
     * @param quantity
     */
    public void create(int startingIndex, int quantity)
    {
        values.addIndexes(startingIndex, quantity);
        notifyTableChanged();
    }


    /**
     * @see #getValueImpl(int)
     * @param address
     * @return the value of the specified register
     * @deprecated modify method name because it is overridden by ModbusCoils
     */
    @Deprecated
    public int getRegisterImpl(int address)
    {
        return getValueImpl(address);
    }

    /**
     * @see #getValue(int)
     * @param index
     * @return the value of the specified register
     * @deprecated modify method name because it is overridden by ModbusCoils
     */
    @Deprecated
    public int getRegister(int index)
    {
        return getValue(index);
    }

    /**
     * Returns the value of the register, whose address is provided
     * in argument. Note that depending on the implementation (modbus/jbus),
     * an offset is applied to address.
     * After the offset has been applied, returns the same result as getValue()
     * @param address the address of the register
     * @return see getValue() for info.
     */
    public int getValueImpl(int address)
    {
        address -= getOffset();
        return getValue(address);
    }

    /**
     * Returns the value of the register, whose index is provided
     * in argument. Note that index always starts from 0, with no
     * consideration for the implementation offset (modbus/jbus). If the
     * register is bound to an automation, returns the current value of the
     * automation.
     * @param index the index of the register, starting from 0
     * @return value of the register. If the register doesn't exist, returns 0
     * by default.
     */
    public int getValue(int index)
    {
        Binding binding = bindings.get(index);
        if( binding != null )
        {
            return getValue(binding);
        }
        else
        {
            return values.getByIndex(index);
        }
    }

    /**
     * Returns the value provided by the binding.
     * @param binding the binding that provides the value
     * @return the value given by the binding.
     */
    protected int getValue(Binding binding)
    {
        return binding.getRegister();
    }

    /**
     * Writes a range of values into the provided byte buffer.
     * @param startingIndex the starting index of the values to write
     * @param quantity the number of values to write
     * @param buffer the byte buffer where to write the values
     * @param offset the offset where to start writing in the byte buffer
     * @return XC_SUCCESSFUL if operation is successful
     */
    public byte getValues(int startingIndex, int quantity, byte[] buffer, int offset)
    {
        for(int i=0; i<quantity; i++)
        {
            Integer reg = getValue(startingIndex+i);
            ModbusTools.setUint16(buffer, offset+(2*i), reg);
        }
        return XC_SUCCESSFUL;
    }


    private void detachAllBindings()
    {
        Collection<Binding> collection = bindings.values();
        for( Binding b:collection)
        {
            b.detach();
        }
    }

    void clear()
    {
        values.clear();
        names.clear();
        detachAllBindings();
        bindings.clear();
        notifyTableChanged();
    }


    /**
     * Sets the value of the register identified by the specified index. Note that
     * indexes start from 0, whatever the slave's modbus implementation
     * is (modbus,jbus,...). If the register is bound to an automation, this method
     * has no effect on the current value of the automation.
     * If index does not exist, an error code is returned and no action is performed.
     * This method does not trigger events to refresh the GUI.
     * @param index
     * @param val
     * @return
     */
    byte setValueSilent(int index, int val)
    {
        if( values.indexExists(index) == false )
        {
            return XC_ILLEGAL_DATA_ADDRESS;
        }
        values.putByIndex(index,val);
        return XC_SUCCESSFUL;
    }


    /**
     * @see #setValueImpl(int, int)
     * @param address
     * @param val
     * @return XC_SUCCESSFUL if successful
     * @deprecated name changed because this method is overridden by ModbusCoils
     */
    @Deprecated
    public byte setRegisterImpl(int address, int val)
    {
        return setValueImpl(address,val);
    }

    /**
     * Sets the value of the register identified by the specified address.
     * Note that depending on the implementation (modbus/jbus),
     * an offset is applied to address.
     * After the offset has been applied, returns the same result as setValue()
     * @param address the address of the register
     * @param val the value of the register
     * @return see setValue() for info.
     */
    public byte setValueImpl(int address, int val)
    {
        address -= getOffset();
        return setValue(address,val);
    }

    /**
     * @see #setValue(int, int)
     * @param index
     * @param val
     * @return XC_SUCCESSFUL if successful
     * @deprecated name changed because this method is overridden by ModbusCoils
     */
    @Deprecated
    public byte setRegister(int index, int val)
    {
        return setValue(index,val);
    }

    /**
     * Sets the value of the register identified by the specified index. Note that
     * indexEs start from 0, whatever the slave's modbus implementation
     * is (modbus,jbus,...). If the register is bound to an automation, this method
     * has no effect on the current value of the automation.
     * If index does not exist, an error code is returned and no action is performed.
     * This method will trigger a TableEvent event, so that the GUI is refreshed.
     * @param index
     * @param val
     * @return the modbus error code indicating the success of the failure of the
     * action. In case of success, the returned value is XC_SUCCESSFUL (0x00).
     */
    public byte setValue(int index, int val)
    {
        byte retval = setValueSilent(index,val);
        if( retval==XC_SUCCESSFUL )
        {
            notifyTableChanged(rowIndexOf(index), VALUE_COLUMN_INDEX);
        }
        return retval;
    }

    /**
     * Reads the content of the byte buffer, which contains a range of values to update
     * @param startingAddress the starting address of the range
     * @param quantity the number of values to update
     * @param buffer the byte buffer containing the values
     * @param offset the offset in the buffer where the values start
     * @return XC_SUCCESSFUL if operation successful
     */
    public byte setValues(int startingAddress, int quantity, byte[] buffer, int offset)
    {
        byte retval = XC_SUCCESSFUL;
        for(int i=0; i<quantity; i++)
        {
            Integer reg = ModbusTools.getUint16(buffer, offset + 2* i);
            retval = setValueSilent(startingAddress+i,reg);
            if( retval != XC_SUCCESSFUL )
            {
                break;
            }
        }
        notifyTableChanged();
        return retval;
    }

    /**
     * Convert the index of a value into the index of the row where this
     * value is displayed in the TableModel.
     * @param index index of the value
     * @return index of the row where the value is displayed
     */
    public int rowIndexOf(int index)
    {
        return values.getOrderOf(index);
    }

    private void set(int index, Integer value, String name, Binding binding)
    {
        // set the value of the register
        if( value == null )
        {
            value = 0;
        }
        values.putByIndex(index, value);

        // set the name of the register
        if( name != null )
        {
            names.put(index, name );
        }

        // set the binding of the register
        if( binding != null )
        {
            Binding old = bindings.get(index);
            if( old!=null )
            {
                old.detach();
            }
            binding.attach(this,index);
            bindings.put(index, binding );
        }

        notifyTableChanged( values.getOrderOf(index) );
    }


    /**
     * Checks if the given values are defined, but do not create them
     * if they don't exist.
     * @see #exist(int, int, boolean)
     * @param startingIndex
     * @param quantity
     * @return true if all values exist, false otherwise.
     */
    public boolean exist(int startingIndex, int quantity)
    {
        return exist(startingIndex, quantity, false);
    }

    /**
     * Check if a range of registers, defined by the starting index
     * and the quantity of registers in the range, is already defined
     * or not. This method does not take the selected implementation into
     * account. The first value is always at index 0.
     * @param startingIndex the index of the first value in the range,
     * @param quantity the number of values to check.
     * @param createIfNotExist if true, the values are created if they don't exist
     * @return true if all registers comprised in the range are already defined;
     * false if any register in the range is not defined yet.
     */
    public boolean exist(int startingIndex, int quantity, boolean createIfNotExist)
    {
        assert( startingIndex >= 0 );
        assert( quantity >= 0 );

        for( int i=0; i<quantity; i++ )
        {
            int address = startingIndex + i;
            if( exist(address) == false )
            {
                if( createIfNotExist==true )
                {
                    create(address, 1);
                }
                else
                {
                    return false;
                }
            }
        }
        return true;
    }


    /**
     * Check if a particular register, defined by its index,
     * is already defined or not.
     * @param index
     * @return true if the register is already defined,
     * false otherwise
     */
    public boolean exist(int index)
    {
        assert( index >= 0 );

        if( values.indexExists(index) == false )
        {
            return false;
        }
        return true;
    }


    /**
     * Removes a value from this object. The index is not sensitive to the
     * selected implementation. The first index is always 0.
     * @param index index of the value to remove
     */
    public void remove(int index)
    {
        // check if a binding exists
        Binding binding = bindings.get( index );
        if( binding != null )
        {
            binding.detach();
            bindings.remove(index);
        }

        // delete register
        values.delete( index );
        names.remove( index );
        notifyTableChanged();
    }




    void replace(ModbusRegisters source, int srcIndex, int dstIndex)
    {
        Integer value = source.values.getByIndex(srcIndex);
        String name = source.names.get(srcIndex);
        Binding binding = source.bindings.get(srcIndex);
        set(dstIndex, value, name, binding);
    }


    void paste(int destIndex, RegisterCopy src)
    {
        try
        {
            Binding b = null;
            if( src.registerBinding!=null )
            {
                b = (Binding) src.registerBinding.clone();
            }
            set( destIndex, src.registerValue, src.registerName, b);
        }
        catch(CloneNotSupportedException ex)
        {
            ex.printStackTrace();
        }
    }

    RegisterCopy copy (ModbusRegisters source, int sourceIndex)
    {
        // AddIndexes the register if necessary
        if( values.indexExists(sourceIndex) == false )
        {
            return null;
        }

        Integer value = source.values.getByIndex(sourceIndex);
        String name = source.names.get(sourceIndex);
        Binding binding = source.bindings.get(sourceIndex);
        return new RegisterCopy( sourceIndex, value, name, binding );
    }


    //==========================================================================
    //
    // BINDINGS
    //
    //==========================================================================

    /**
     * Associates the register identified by its index with the
     * provided binding. Note that indexes start from 0,
     * whatever the slave's modbus implementation is (modbus,jbus,...). If a binding
     * already exists, it is replaced by the new.
     * @param index
     * @param binding
     */
    public void bind(int index, Binding binding)
    {
        bindings.put(index, binding);
        int row = values.getOrderOf(index);
        notifyTableChanged(row);
        binding.attach(this,index);
    }

    /**
     * Removes the binding for the value (identified by its index) and whatever
     * automation it is linked to.
     * @param index index of the value from which the binding must be removed.
     */
    public void unbind(int index)
    {
        Binding removed = bindings.remove(index);
        if( removed!=null )
        {
            removed.detach();
            notifyTableChanged( values.getOrderOf(index) );
        }
    }


    /**
     * Checks if any of the registers has a binding definition.
     * @return true if any of the registers has a binding defined, false if none
     * of the register has a binding.
     */
    public boolean hasBindings()
    {
        return (bindings.size()>0);
    }

    /**
     * Checks if the value, identified by its index, has a binding or not.
     * @param index index of the register to check
     * @return true if the register is bound to an automation.
     */
    public boolean isBound(int index)
    {
        Binding binding = bindings.get(index);
        return (binding!=null);
    }

    /**
     * Removes any bindings that is of the specified class name.
     * @param classname the class name of the bindings to remove
     */
    public void removeAllBindings(String classname)
    {
        Set<Integer> addressesSet = bindings.keySet();
        Integer addressesArray[] = new Integer[0];
        addressesArray = addressesSet.toArray(addressesArray);
        for(int i=0; i<addressesArray.length; i++)
        {
            Integer address = addressesArray[i];
            Binding b = bindings.get(address);
            if( b.getClassName().compareTo(classname)==0 )
            {
                bindings.remove(address);
            }
        }
        notifyTableChanged();
    }

    //==========================================================================
    //
    // TABLE MODEL IMPLEMENTATION
    //
    //==========================================================================

    @Override
    public int getRowCount()
    {
        return values.getCount();
    }

    @Override
    public int getColumnCount()
    {
        return 4;
    }

    @Override
    public String getColumnName(int columnIndex)
    {
        switch(columnIndex)
        {
            case ADDRESS_COLUMN_INDEX: return ADDRESS_COLUMN_NAME;
            case VALUE_COLUMN_INDEX: return "Value";
            case NAME_COLUMN_INDEX: return "Name";
            case BINDING_COLUMN_INDEX: return "Binding";
            default: return "Column " + String.valueOf(columnIndex);
        }
    }

    @Override
    public Class<?> getColumnClass(int columnIndex)
    {
        switch(columnIndex)
        {
            case ADDRESS_COLUMN_INDEX: return Integer.class;
            case VALUE_COLUMN_INDEX: return String.class;
            case NAME_COLUMN_INDEX: return String.class;
            case BINDING_COLUMN_INDEX: return String.class;
            default: return Object.class;
        }
    }

    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex)
    {
        switch(columnIndex)
        {
            case ADDRESS_COLUMN_INDEX: return false;
            case VALUE_COLUMN_INDEX: return !isBound( getAddressOf(rowIndex) );
            case NAME_COLUMN_INDEX: return true;
            case BINDING_COLUMN_INDEX: return false;
            default: return false;
        }
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex)
    {
        int index = values.getIndexOf(rowIndex);
        switch(columnIndex)
        {
            case ADDRESS_COLUMN_INDEX:
            {
                return addressOffset+index;
            }

            case VALUE_COLUMN_INDEX:
            {
                int reg = getValue(index);
                return String.valueOf( reg );
            }
            case NAME_COLUMN_INDEX:
            {
                return names.get(index);
            }
            case BINDING_COLUMN_INDEX:
            {
                Binding binding = bindings.get(index);
                if( binding!=null )
                {
                    return binding.toString();
                }
                else
                {
                    return null;
                }
            }
        }
        return null;
    }


    /**
     * Checks if the specified value is within the allowed boundaries. For a
     * MODBUS register, the allowed range is [0-65535]. For a coil, it is [0-1].
     * @param value the value to check
     * @return true if the value is within boundaries.
     */
    protected Integer checkValueBoundaries(Integer value)
    {
        if( value<0 )
        {
            return 0;
        }
        if( value>65535 )
        {
            return 65535;
        }
        return value;
    }



    @Override
    public void setValueAt(Object aValue, int rowIndex, int columnIndex)
    {
        int index = values.getIndexOf(rowIndex);
        switch( columnIndex )
        {
            case VALUE_COLUMN_INDEX:
            {
                if( aValue instanceof String )
                {
                    Integer val = Integer.parseInt((String)aValue);
                    values.putByIndex(index, checkValueBoundaries(val));
                    notifyTableChanged(rowIndex,columnIndex);
                }
                break;
            }

            case NAME_COLUMN_INDEX:
            {
                if( aValue instanceof String )
                {
                    String val = (String)aValue;
                    names.put(index, val);
                    notifyTableChanged(rowIndex,columnIndex);
                }
                break;
            }
        }
    }

    @Override
    public void addTableModelListener(TableModelListener l)
    {
        tableModelListeners.add(l);
    }

    @Override
    public void removeTableModelListener(TableModelListener l)
    {
        tableModelListeners.remove(l);
    }

    //==========================================================================
    //
    // OTHER PROPERTIES
    //
    //==========================================================================


    /**
     * Gets the name that has been defined for the specified value, identified
     * by its index.
     * @param index index of the value
     * @return name given to the specified value
     */
    public String getName(int index)
    {
        return names.get(index);
    }

    /**
     * Converts the row from the TableModel into the corresponding value index
     * @param row the row index from the table model
     * @return the value index
     */
    public int getAddressOf(int row)
    {
        int index = values.getIndexOf(row);
        return index;
    }

    /**
     * Gets a list of all the automations that are being used by this
     * object, through the declared bindings.
     * @return a collection of strings containing the classnames of the automations
     * in use in this object.
     */
    Collection<String> getRequiredAutomations()
    {
        ArrayList<String> automationNames = new ArrayList<String>();
        Collection<Binding> en = bindings.values();
        for(Binding b:en)
        {
            String n = b.getAutomationName();
            if( automationNames.contains(n)==false )
            {
                automationNames.add(n);
            }
        }
        return automationNames;
    }

    void setOffset(int offset)
    {
        if( offset != addressOffset )
        {
            addressOffset = offset;
            notifyTableChanged();
        }
    }

    int getOffset()
    {
        return addressOffset;
    }


    //==========================================================================
    //
    // FILE INPUTS/OUTPUTS
    //
    //==========================================================================

   
    /**
     * Saves the values and settings of this object.
     * @param out the output stream where to write the values and settings
     * @param withBindings if true, the bindings of each value are also
     * described in the output stream.
     * @throws IOException
     */
    public void save(OutputStream out, boolean withBindings)
    throws IOException
    {
        for( int i=0; i<values.getCount(); i++ )
        {
            int index = values.getIndexOf(i);

            StringBuilder tag = new StringBuilder("<"+TXT_REGISTER+" "+ XML_ADDRESS_ATTRIBUTE +"=\"");
            tag.append(String.valueOf(index));
            tag.append("\" value=\"");
           
            int val = values.getByIndex(index);
            tag.append(String.valueOf(val));
            tag.append("\"");

            String name= names.get(index);
            if( name != null )
            {
                tag.append(" name=\"");
                tag.append(name);
                tag.append("\"");
            }

            Binding binding = bindings.get(index);
            if( (binding==null) || (withBindings==false) )
            {
                tag.append("/>\r\n");
                out.write( tag.toString().getBytes() );
            }
            else
            {
                tag.append(">\r\n");
                out.write( tag.toString().getBytes() );
                binding.save(out);
                String closeTag = "</"+TXT_REGISTER+">\r\n";
                out.write( closeTag.getBytes() );
            }
        }
    }

    /**
     * Loads the values and settings from the provided DOM node.
     * @param node the node containing the values and settings to load.
     */
    public void load(Node node)
    {
        NodeList nodes = node.getChildNodes();
        for(int i=0; i<nodes.getLength(); i++ )
        {
            Node child = nodes.item(i);
            if( child.getNodeName().compareTo(TXT_REGISTER)==0 )
            {
                loadRegister(child);
            }
        }
        notifyTableChanged();
    }

    private void loadRegister(Node node)
    {
        NamedNodeMap attributes = node.getAttributes();
        Integer address = null;
        Integer value = null;
        String name = null;

        for(int i=0; i<attributes.getLength(); i++)
        {
            Node attr = attributes.item(i);
            String nodeName = attr.getNodeName();

            if( nodeName.compareTo(XML_ADDRESS_ATTRIBUTE)==0 )
            {
                address = new Integer( attr.getNodeValue() );
            }
            else if( nodeName.compareTo("value")==0 )
            {
                value = new Integer( attr.getNodeValue() );
            }
            else if( nodeName.compareTo("name")==0 )
            {
                name = attr.getNodeValue();
            }
        }

        // TODO: throw an exception if those assertions are false.
        assert( address != null );
        assert( value != null );

        if( values.indexExists(address)==false )
        {
            values.addIndex(address);
        }

        values.putByIndex( address, value );

        if( name != null )
        {
            names.put(address, name);
        }

        // NOTE: bindings are loaded later, see MainGui::loadBindings()
    }



    @Override
    public void savePduProcessorSettings(OutputStream out)
    throws IOException
    {
        throw new UnsupportedOperationException("not implemented");
    }

    @Override
    public void loadPduProcessorSettings(NodeList list)
    {
        throw new UnsupportedOperationException("not implemented");
    }


    //==========================================================================
    //
    // EVENTS MANAGEMENT
    //
    //==========================================================================


    /**
     * Triggers a "tableChanged" event for the registered TableModelListeners.
     */
    public void notifyTableChanged()
    {
        TableModelEvent event = new TableModelEvent(this);
        for(TableModelListener l:tableModelListeners)
        {
            l.tableChanged(event);
        }
    }

    /**
     * Triggers a "tableChanged" event for the registered TableModelListeners.
     * The event specifically addresses the row where the modified value is
     * displayed.
     * @param valueIndex index of the modified value (register/coil)
     */
    public void notifyRegisterChanged(int valueIndex)
    {
        int index = rowIndexOf(valueIndex);
        notifyTableChanged(index);
    }

    private void notifyTableChanged(int rowIndex)
    {
        TableModelEvent event = new TableModelEvent(this,rowIndex);
        for(TableModelListener l:tableModelListeners)
        {
            l.tableChanged(event);
        }
    }


    private void notifyTableChanged(int rowIndex, int columnIndex)
    {
        TableModelEvent event = new TableModelEvent(this, rowIndex, rowIndex, columnIndex);
        for(TableModelListener l:tableModelListeners)
        {
            l.tableChanged(event);
        }
    }

}
TOP

Related Classes of modbuspal.slave.ModbusRegisters$RegisterCopy

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.