You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by jv...@apache.org on 2011/03/24 12:35:28 UTC

svn commit: r1084906 [1/2] - in /mina/sandbox/jvermillard/mina-modbus: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/mina/ src/main/java/org/apache/mina/modbus/ src/main/java/org/apache/mina/modb...

Author: jvermillard
Date: Thu Mar 24 11:35:26 2011
New Revision: 1084906

URL: http://svn.apache.org/viewvc?rev=1084906&view=rev
Log:
modbus stack need to fix test case and add javadoc

Added:
    mina/sandbox/jvermillard/mina-modbus/pom.xml   (with props)
    mina/sandbox/jvermillard/mina-modbus/src/
    mina/sandbox/jvermillard/mina-modbus/src/main/
    mina/sandbox/jvermillard/mina-modbus/src/main/java/
    mina/sandbox/jvermillard/mina-modbus/src/main/java/org/
    mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/
    mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/
    mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/
    mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/ModbusConstants.java   (with props)
    mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/ModbusSlave.java   (with props)
    mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/ModbusTable.java   (with props)
    mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/msg/
    mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/msg/ModbusMessage.java   (with props)
    mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/
    mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/ModbusTransport.java   (with props)
    mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/rtu/
    mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/rtu/RTUModbusDecoder.java   (with props)
    mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/rtu/RTUModbusEncoder.java   (with props)
    mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/rtu/RTUModbusMessage.java   (with props)
    mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/rtu/RTUModbusTransport.java   (with props)
    mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/tcp/
    mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/tcp/TCPModbusDecoder.java   (with props)
    mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/tcp/TCPModbusEncoder.java   (with props)
    mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/tcp/TCPModbusMessage.java   (with props)
    mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/tcp/TCPModbusTransport.java   (with props)
    mina/sandbox/jvermillard/mina-modbus/src/test/
    mina/sandbox/jvermillard/mina-modbus/src/test/java/
    mina/sandbox/jvermillard/mina-modbus/src/test/java/org/
    mina/sandbox/jvermillard/mina-modbus/src/test/java/org/apache/
    mina/sandbox/jvermillard/mina-modbus/src/test/java/org/apache/mina/
    mina/sandbox/jvermillard/mina-modbus/src/test/java/org/apache/mina/modbus/
    mina/sandbox/jvermillard/mina-modbus/src/test/java/org/apache/mina/modbus/transport/
    mina/sandbox/jvermillard/mina-modbus/src/test/java/org/apache/mina/modbus/transport/rtu/
    mina/sandbox/jvermillard/mina-modbus/src/test/java/org/apache/mina/modbus/transport/rtu/RTUModbusDecoderMasterTest.java   (with props)
    mina/sandbox/jvermillard/mina-modbus/src/test/java/org/apache/mina/modbus/transport/rtu/RTUModbusDecoderTest.java   (with props)
    mina/sandbox/jvermillard/mina-modbus/src/test/java/org/apache/mina/modbus/transport/rtu/RTUModbusEncoderTest.java   (with props)
    mina/sandbox/jvermillard/mina-modbus/src/test/java/org/apache/mina/modbus/transport/tcp/
    mina/sandbox/jvermillard/mina-modbus/src/test/java/org/apache/mina/modbus/transport/tcp/TCPModbusDecoderMasterTest.java   (with props)
    mina/sandbox/jvermillard/mina-modbus/src/test/java/org/apache/mina/modbus/transport/tcp/TCPModbusDecoderTest.java   (with props)
    mina/sandbox/jvermillard/mina-modbus/src/test/java/org/apache/mina/modbus/transport/tcp/TCPModbusEncoderTest.java   (with props)
Modified:
    mina/sandbox/jvermillard/mina-modbus/   (props changed)

Propchange: mina/sandbox/jvermillard/mina-modbus/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Thu Mar 24 11:35:26 2011
@@ -0,0 +1 @@
+target

Added: mina/sandbox/jvermillard/mina-modbus/pom.xml
URL: http://svn.apache.org/viewvc/mina/sandbox/jvermillard/mina-modbus/pom.xml?rev=1084906&view=auto
==============================================================================
--- mina/sandbox/jvermillard/mina-modbus/pom.xml (added)
+++ mina/sandbox/jvermillard/mina-modbus/pom.xml Thu Mar 24 11:35:26 2011
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+  http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>org.apache.mina</groupId>
+  <version>2.0.3-SNAPSHOT</version>
+  <artifactId>mina-modbus</artifactId>
+  <name>Apache MINA Modbus stack</name>
+  <packaging>bundle</packaging>  
+
+  <properties>
+    <symbolicName>${project.groupId}.modbus</symbolicName>
+    <exportedPackage>${project.groupId}.modbus</exportedPackage>
+  </properties>
+
+
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>2.3.2</version>
+        <configuration>
+          <encoding>UTF-8</encoding>
+          <source>1.5</source>
+          <target>1.5</target>
+          <debug>true</debug>
+          <optimize>true</optimize>
+          <showDeprecations>true</showDeprecations>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <version>2.7.1</version>
+        <configuration>
+          <excludes>
+            <exclude>**/Abstract*</exclude>
+            <exclude>**/*RegressionTest*</exclude>
+          </excludes>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <artifactId>maven-source-plugin</artifactId>
+        <version>2.1.2</version>
+        <executions>
+          <execution>
+            <id>attach-source</id>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <version>2.2.0</version>
+        <inherited>true</inherited>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-SymbolicName>${symbolicName}</Bundle-SymbolicName>
+            <Export-Package>${exportedPackage}.*;version=${project.version}</Export-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>mina-core</artifactId>
+      <version>2.0.2</version>
+      <type>bundle</type>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>1.6.1</version>
+    </dependency>
+	<dependency>
+	  <groupId>org.slf4j</groupId>
+	  <artifactId>slf4j-log4j12</artifactId>
+	  <version>1.6.1</version>
+      <scope>test</scope>
+	</dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.8.2</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
+
+

Propchange: mina/sandbox/jvermillard/mina-modbus/pom.xml
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/ModbusConstants.java
URL: http://svn.apache.org/viewvc/mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/ModbusConstants.java?rev=1084906&view=auto
==============================================================================
--- mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/ModbusConstants.java (added)
+++ mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/ModbusConstants.java Thu Mar 24 11:35:26 2011
@@ -0,0 +1,48 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.mina.modbus;
+
+public interface ModbusConstants {
+    public static final int READ_DISCRETE_INPUTS = 02;
+
+    public static final int READ_COILS = 01;
+
+    public static final int WRITE_SINGLE_COILS = 05;
+
+    public static final int WRITE_MULTIPLE_COILS = 15;
+
+    public static final int READ_INPUT_REGISTERS = 04;
+
+    public static final int READ_HOLDING_REGISTERS = 03;
+
+    public static final int WRITE_SINGLE_REGISTER = 06;
+
+    public static final int WRITE_MULTIPLE_REGISTERS = 16;
+
+    public static final int READ_WRITE_MULTIPLE_REGISTERS = 23;
+
+    public static final int MASK_WRITE_REGISTER = 22;
+
+    public static final int READ_FIFO_QUEUE = 24;
+
+    public static final int READ_FILE_RECORD = 20;
+
+    public static final int WRITE_FILE_RECORD = 21;
+}

Propchange: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/ModbusConstants.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/ModbusSlave.java
URL: http://svn.apache.org/viewvc/mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/ModbusSlave.java?rev=1084906&view=auto
==============================================================================
--- mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/ModbusSlave.java (added)
+++ mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/ModbusSlave.java Thu Mar 24 11:35:26 2011
@@ -0,0 +1,281 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.mina.modbus;
+
+import org.apache.mina.core.service.IoConnector;
+import org.apache.mina.core.service.IoHandler;
+import org.apache.mina.core.session.IdleStatus;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.filter.codec.ProtocolCodecFactory;
+import org.apache.mina.filter.codec.ProtocolCodecFilter;
+import org.apache.mina.filter.codec.ProtocolDecoder;
+import org.apache.mina.filter.codec.ProtocolEncoder;
+import org.apache.mina.modbus.msg.ModbusMessage;
+import org.apache.mina.modbus.transport.ModbusTransport;
+import org.apache.mina.modbus.transport.rtu.RTUModbusMessage;
+import org.apache.mina.modbus.transport.rtu.RTUModbusTransport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * 
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ */
+public class ModbusSlave {
+
+    private String name;
+
+    private ModbusTable table;
+
+    private boolean forceInitCard = false;
+
+    private Logger log;
+
+    private Handler handler = new Handler();
+
+    private ModbusTransport transport;
+
+    private Integer deviceId;
+
+    /**
+     * @param name
+     * @param hostname
+     * @param port
+     * @param table
+     */
+    public ModbusSlave(String name, ModbusTable table, ModbusTransport transport) {
+        this(name, null, table, transport);
+    }
+
+    public ModbusSlave(String name, Integer deviceId, ModbusTable table, ModbusTransport transport) {
+        super();
+        this.name = name;
+        this.deviceId = deviceId;
+        this.table = table;
+        this.transport = transport;
+        log = LoggerFactory.getLogger(ModbusSlave.class);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    IoConnector connector = null;
+
+    public void start() {
+        table.start();
+        transport.connect(handler);
+    }
+
+    public void stop() {
+
+    }
+
+    private class Handler implements IoHandler {
+
+        public Handler() {
+
+        }
+
+        public void sessionCreated(IoSession session) throws Exception {
+            session.getFilterChain().addLast("protocolCodec", new ProtocolCodecFilter(new ProtocolCodecFactory() {
+
+                public ProtocolDecoder getDecoder(IoSession session) {
+                    return transport.createDecoder(false);
+                }
+
+                public ProtocolEncoder getEncoder(IoSession session) {
+                    return transport.createEncoder();
+                }
+            }));
+
+        }
+
+        public void sessionOpened(IoSession session) throws Exception {
+            log.info("Connection opened : " + session.getRemoteAddress());
+
+            if (forceInitCard && (transport instanceof RTUModbusTransport)) {
+                session.write(new RTUModbusMessage(1, ModbusConstants.READ_COILS));
+            }
+        }
+
+        public void sessionClosed(IoSession session) throws Exception {
+            log.info("Connection closed : " + session.getRemoteAddress());
+            if (transport instanceof RTUModbusTransport) {
+                transport.connect(handler);
+            }
+        }
+
+        public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
+            return;
+        }
+
+        public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
+            log.error("Exception : ", cause);
+            session.close(true);
+        }
+
+        public void messageReceived(IoSession session, Object message) throws Exception {
+            if (log.isDebugEnabled()) {
+                log.debug("RCVD : " + message);
+            }
+            if (message instanceof ModbusMessage) {
+
+                ModbusMessage msg = (ModbusMessage) message;
+                if ((deviceId != null) && (msg.getDevice() != deviceId)) {
+                    return;
+                }
+                if ((msg.getFunctionCode() == ModbusConstants.READ_HOLDING_REGISTERS)
+                        || (msg.getFunctionCode() == ModbusConstants.READ_INPUT_REGISTERS)) {
+
+                    ModbusMessage reply = transport.createReplyMessage(msg);
+
+                    // reading some words
+                    int add = ((msg.getData()[0] & 0xff) << 8) + (msg.getData()[1] & 0xff);
+                    int count = (msg.getData()[2] << 8) + ((msg.getData()[3]) & 0xFF);
+                    if (isQuantityValidToRead(count)) {
+                        if (!checkError || table.canAccessDataToRead(add, count)) {
+                            byte[] res = new byte[count * 2 + 1];
+                            res[0] = (byte) (count * 2);
+                            for (int i = 0; i < count; i++) {
+                                int word = table.readWord(add + i);
+                                res[i * 2 + 1] = (byte) (word >> 8);
+                                res[i * 2 + 2] = (byte) (word & 0xFF);
+                            }
+
+                            // sendback
+                            reply.setData(res);
+                        } else {
+                            reply.setFunctionCode(reply.getFunctionCode() + 0x80);
+                            reply.setData(new byte[] { 0x02 });
+                        }
+                    } else {
+                        reply.setFunctionCode(reply.getFunctionCode() + 0x80);
+                        reply.setData(new byte[] { 0x03 });
+                    }
+
+                    session.write(reply);
+
+                } else if (msg.getFunctionCode() == ModbusConstants.WRITE_SINGLE_REGISTER) {
+
+                    ModbusMessage reply = transport.createReplyMessage(msg);
+
+                    // writing a word
+                    int add = ((msg.getData()[0] & 0xFF) << 8) + (msg.getData()[1] & 0xFF);
+                    int value = ((msg.getData()[2] & 0xFF) << 8) + ((msg.getData()[3]) & 0xFF);
+                    if (isValueValid(value)) {
+                        if (!checkError || table.canAccessDataToWrite(add, 1)) {
+                            table.writeWord(add, value);
+
+                            // sendback
+                            reply.setData(msg.getData());
+                        } else {
+                            reply.setFunctionCode(reply.getFunctionCode() + 0x80);
+                            reply.setData(new byte[] { 0x02 });
+                        }
+                    } else {
+                        reply.setFunctionCode(reply.getFunctionCode() + 0x80);
+                        reply.setData(new byte[] { 0x03 });
+                    }
+                    session.write(reply);
+
+                } else if (msg.getFunctionCode() == ModbusConstants.WRITE_MULTIPLE_REGISTERS) {
+
+                    // writing multiple words
+                    int add = ((msg.getData()[0] & 0xFF) << 8) + (msg.getData()[1] & 0xFF);
+                    int count = (msg.getData()[2] << 8) + ((msg.getData()[3]) & 0xFF);
+                    int byteCount = msg.getData()[4];
+                    // reply
+                    ModbusMessage reply = transport.createReplyMessage(msg);
+                    boolean validity = isQuantityValidToWrite(count, byteCount);
+                    boolean accessOK = false;
+                    if (validity) {
+                        accessOK = !checkError || table.canAccessDataToWrite(add, count);
+                        if (accessOK) {
+                            reply.setData(new byte[] { msg.getData()[0], msg.getData()[1], msg.getData()[2],
+                                    msg.getData()[3] });
+                        } else {
+                            reply.setFunctionCode(reply.getFunctionCode() + 0x80);
+                            reply.setData(new byte[] { 0x02 });
+                        }
+                    } else {
+                        reply.setFunctionCode(reply.getFunctionCode() + 0x80);
+                        reply.setData(new byte[] { 0x03 });
+                    }
+                    session.write(reply);
+
+                    if (validity && accessOK) {
+                        for (int i = 0; i < count; i++) {
+                            // inverted MSB/LSB
+                            table.writeWord(add + i, ((msg.getData()[5 + (i * 2)] & 0xFF) << 8)
+                                    + (((msg.getData()[5 + (i * 2) + 1]) & 0xFF)));
+                        }
+                    }
+                } else {
+                    if (checkError) {
+                        // reply Error message function code unsupported
+                        ModbusMessage reply = transport.createReplyMessage(msg);
+                        reply.setFunctionCode(reply.getFunctionCode() + 0x80);
+                        reply.setData(new byte[] { 0x01 });
+                        session.write(reply);
+                    }
+                }
+            }
+        }
+
+        public void messageSent(IoSession session, Object message) throws Exception {
+            if (log.isDebugEnabled()) {
+                log.debug("SENT : " + message);
+            }
+        }
+    }
+
+    public void setForceInitCard(boolean forceInitCard) {
+        this.forceInitCard = forceInitCard;
+    }
+
+    private boolean isValueValid(int value) {
+        if ((value >= 0) && (value <= 0xFFFF)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean isQuantityValidToWrite(int quantity, int byteCount) {
+        if (!checkError || ((quantity > 0) && (quantity <= 0x7B) && (byteCount == quantity * 2))) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean isQuantityValidToRead(int quantity) {
+        if (!checkError || ((quantity > 0) && (quantity <= 0x7D))) {
+            return true;
+        }
+        return false;
+    }
+
+    boolean checkError = false;
+
+    public void enableCheckError() {
+        checkError = true;
+    }
+}

Propchange: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/ModbusSlave.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/ModbusTable.java
URL: http://svn.apache.org/viewvc/mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/ModbusTable.java?rev=1084906&view=auto
==============================================================================
--- mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/ModbusTable.java (added)
+++ mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/ModbusTable.java Thu Mar 24 11:35:26 2011
@@ -0,0 +1,43 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.mina.modbus;
+
+/**
+ * A modbus table. This table is a slave and reply with values to the request of the master.
+ * 
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ *
+ */
+public interface ModbusTable {
+    
+    public void writeWord(int address, int value);
+
+    public int readWord(int address);
+
+    public void writeCoil(int address, boolean value);
+
+    public boolean readCoil(int address);
+
+    public boolean canAccessDataToRead(int addressStart, int count);
+
+    public boolean canAccessDataToWrite(int addressStart, int count);
+
+    public void start();
+}

Propchange: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/ModbusTable.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/msg/ModbusMessage.java
URL: http://svn.apache.org/viewvc/mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/msg/ModbusMessage.java?rev=1084906&view=auto
==============================================================================
--- mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/msg/ModbusMessage.java (added)
+++ mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/msg/ModbusMessage.java Thu Mar 24 11:35:26 2011
@@ -0,0 +1,33 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.mina.modbus.msg;
+
+public interface ModbusMessage {
+
+    public int getDevice();
+
+    public int getFunctionCode();
+
+    public void setFunctionCode(int functionCode);
+
+    public byte[] getData();
+
+    public void setData(byte[] data);
+}

Propchange: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/msg/ModbusMessage.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/ModbusTransport.java
URL: http://svn.apache.org/viewvc/mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/ModbusTransport.java?rev=1084906&view=auto
==============================================================================
--- mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/ModbusTransport.java (added)
+++ mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/ModbusTransport.java Thu Mar 24 11:35:26 2011
@@ -0,0 +1,37 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.mina.modbus.transport;
+
+import org.apache.mina.core.service.IoHandler;
+import org.apache.mina.filter.codec.ProtocolDecoder;
+import org.apache.mina.filter.codec.ProtocolEncoder;
+import org.apache.mina.modbus.msg.ModbusMessage;
+
+public interface ModbusTransport {
+    ProtocolEncoder createEncoder();
+
+    ProtocolDecoder createDecoder(boolean master);
+
+    void connect(IoHandler handler);
+
+    ModbusMessage createReplyMessage(ModbusMessage query);
+
+    ModbusMessage createMessage(int functionCode, int unitId);
+}

Propchange: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/ModbusTransport.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/rtu/RTUModbusDecoder.java
URL: http://svn.apache.org/viewvc/mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/rtu/RTUModbusDecoder.java?rev=1084906&view=auto
==============================================================================
--- mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/rtu/RTUModbusDecoder.java (added)
+++ mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/rtu/RTUModbusDecoder.java Thu Mar 24 11:35:26 2011
@@ -0,0 +1,200 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.mina.modbus.transport.rtu;
+
+import org.apache.log4j.Logger;
+
+import java.nio.ByteOrder;
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
+import org.apache.mina.filter.codec.ProtocolDecoderException;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+import org.apache.mina.modbus.ModbusConstants;
+import org.apache.mina.modbus.transport.rtu.RTUModbusMessage.CRCException;
+
+public class RTUModbusDecoder extends CumulativeProtocolDecoder {
+
+    private static final Logger LOG = Logger.getLogger("RTUModbusDecoder");
+
+    private boolean master;
+
+    public RTUModbusDecoder(boolean master) {
+        this.master = master;
+    }
+
+    @Override
+    protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
+
+        in.order(ByteOrder.BIG_ENDIAN);
+
+        int startPos = in.position();
+        boolean isDecoded = false;
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("doDecode bytes : " + in);
+        }
+
+        if (in.remaining() < 2) {
+            in.position(startPos);
+            return false;
+        }
+
+        if (master) {
+            isDecoded = decodeReply(in, out);
+        } else {
+            isDecoded = decodeQuery(in, out);
+        }
+
+        if (isDecoded) {
+            return true;
+        }
+
+        in.position(startPos);
+        return false;
+    }
+
+    private boolean decodeReply(IoBuffer in, ProtocolDecoderOutput out) throws CRCException, ProtocolDecoderException {
+
+        RTUModbusMessage msg;
+
+        int startPos = in.position();
+
+        int function = in.getUnsigned(startPos + 1);
+
+        // check for Modbus error
+        if ((function & 0x80) > 0) {
+            int fct = function - 0x80;
+            switch (fct) {
+            case ModbusConstants.READ_HOLDING_REGISTERS:
+            case ModbusConstants.READ_INPUT_REGISTERS:
+            case ModbusConstants.WRITE_SINGLE_REGISTER:
+            case ModbusConstants.WRITE_MULTIPLE_REGISTERS:
+
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Found a modbus error message");
+                }
+
+                msg = new RTUModbusMessage(in, 5);
+                //decode
+                out.write(msg);
+                return true;
+
+            default:
+                throw new ProtocolDecoderException("Unknown data");
+            }
+        }
+
+        switch (function) {
+        case ModbusConstants.READ_HOLDING_REGISTERS:
+        case ModbusConstants.READ_INPUT_REGISTERS:
+
+            if (in.remaining() < 3) {
+                return false;
+            }
+
+            int dataLen = in.getUnsigned(startPos + 2);
+            if (in.remaining() < dataLen + 5) {
+                return false;
+            }
+
+            msg = new RTUModbusMessage(in, dataLen + 5);
+            //decode
+            out.write(msg);
+            return true;
+
+        case ModbusConstants.WRITE_SINGLE_REGISTER:
+        case ModbusConstants.WRITE_MULTIPLE_REGISTERS:
+            // fixed len
+            if (in.remaining() < 8) {
+                return false;
+            }
+
+            msg = new RTUModbusMessage(in, 8);
+            //decode
+            out.write(msg);
+            return true;
+
+        default:
+            throw new ProtocolDecoderException("Unknown data");
+        }
+    }
+
+    private boolean decodeQuery(IoBuffer in, ProtocolDecoderOutput out) throws CRCException, ProtocolDecoderException {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Decode a query : " + in);
+        }
+        int startPos = in.position();
+
+        int function = in.getUnsigned(startPos + 1);
+
+        RTUModbusMessage msg;
+        switch (function) {
+        case ModbusConstants.READ_HOLDING_REGISTERS:
+        case ModbusConstants.READ_INPUT_REGISTERS:
+        case ModbusConstants.WRITE_SINGLE_REGISTER:
+            // fixed len
+            if (in.remaining() < 8) {
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("no enougth data");
+                }
+                return false;
+            }
+
+            msg = new RTUModbusMessage(in, 8);
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Decoded a Modbus query : " + msg);
+            }
+            out.write(msg);
+
+            return true;
+        case ModbusConstants.WRITE_MULTIPLE_REGISTERS:
+
+            if (in.remaining() < 7) {
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("no enougth data");
+                }
+                return false;
+            }
+
+            int dataLen = in.getUnsigned(startPos + 6);
+
+            if (in.remaining() < dataLen + 9) {
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("no enougth data : remaining " + in.remaining() + " was expecting " + (dataLen + 9));
+                }
+                return false;
+            }
+
+            msg = new RTUModbusMessage(in, dataLen + 9);
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Decoded a Modbus multiple query : " + msg);
+            }
+            //decode
+            out.write(msg);
+
+            return true;
+        default:
+            throw new ProtocolDecoderException("Unknown data");
+        }
+    }
+
+}

Propchange: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/rtu/RTUModbusDecoder.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/rtu/RTUModbusEncoder.java
URL: http://svn.apache.org/viewvc/mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/rtu/RTUModbusEncoder.java?rev=1084906&view=auto
==============================================================================
--- mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/rtu/RTUModbusEncoder.java (added)
+++ mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/rtu/RTUModbusEncoder.java Thu Mar 24 11:35:26 2011
@@ -0,0 +1,51 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.mina.modbus.transport.rtu;
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.filter.codec.ProtocolEncoder;
+import org.apache.mina.filter.codec.ProtocolEncoderException;
+import org.apache.mina.filter.codec.ProtocolEncoderOutput;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class RTUModbusEncoder implements ProtocolEncoder {
+
+    private static final Logger LOG = LoggerFactory.getLogger(RTUModbusEncoder.class);
+
+    public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws ProtocolEncoderException {
+        if (message instanceof RTUModbusMessage) {
+            IoBuffer buf = ((RTUModbusMessage) message).encode();
+
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Out bytes : " + buf.getHexDump());
+            }
+
+            out.write(buf);
+
+        } else {
+            throw new ProtocolEncoderException("Unknow message type : " + message.getClass().getName());
+        }
+    }
+
+    public void dispose(IoSession session) throws Exception {
+    }
+}

Propchange: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/rtu/RTUModbusEncoder.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/rtu/RTUModbusMessage.java
URL: http://svn.apache.org/viewvc/mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/rtu/RTUModbusMessage.java?rev=1084906&view=auto
==============================================================================
--- mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/rtu/RTUModbusMessage.java (added)
+++ mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/rtu/RTUModbusMessage.java Thu Mar 24 11:35:26 2011
@@ -0,0 +1,158 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.mina.modbus.transport.rtu;
+
+import java.nio.ByteOrder;
+import java.security.InvalidParameterException;
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.apache.mina.modbus.msg.ModbusMessage;
+
+
+public class RTUModbusMessage implements ModbusMessage {
+
+    private int functionCode;
+
+    private byte[] data;
+
+    private int device;
+
+    public RTUModbusMessage(int device, int functioncode) {
+        this(device, functioncode, new byte[] {});
+    }
+
+    public RTUModbusMessage(int device, int functioncode, byte[] data) {
+        this.device = device;
+        this.functionCode = functioncode;
+        this.data = data;
+    }
+
+    /**
+     * decode a buffer and create the PDU, test the CRC 
+     * @param buffer
+     * @throws crc exception if the crc is incorrect
+     */
+    public RTUModbusMessage(IoBuffer buffer, int length) throws CRCException {
+        ByteOrder oldOrder = buffer.order();
+        int pduStart = buffer.position();
+
+        buffer.order(ByteOrder.BIG_ENDIAN);
+        device = buffer.getUnsigned(); // 1 Byte device code
+        functionCode = buffer.getUnsigned(); // 1 Byte function code
+        data = new byte[length - 4];
+        buffer.get(data, 0, data.length);
+        buffer.order(ByteOrder.LITTLE_ENDIAN);
+
+        int calculatedCRC = getCrc(buffer, pduStart, pduStart + length - 2);
+
+        int crc = buffer.getUnsignedShort(); // 2 byte CRC
+
+        if (crc != calculatedCRC) {
+            throw new CRCException("Invalid CRC, need " + calculatedCRC + " and got " + crc + ", Dump : "
+                    + buffer.getHexDump());
+        }
+
+        buffer.order(oldOrder);
+    }
+
+    public int getFunctionCode() {
+        return functionCode;
+    }
+
+    public byte[] getData() {
+        return data;
+    }
+
+    public void setData(byte[] data) {
+        this.data = data;
+    }
+
+    public int getDevice() {
+        return device;
+    }
+
+    /**
+     * create a buffer encoding this PDU
+     * @return a new pooled ByteBuffer
+     */
+    public IoBuffer encode() {
+        IoBuffer buff = IoBuffer.allocate(4 + data.length);
+        buff.order(ByteOrder.BIG_ENDIAN);
+        buff.put((byte) device);
+        buff.put((byte) functionCode);
+        buff.put(data);
+        buff.order(ByteOrder.LITTLE_ENDIAN);
+        buff.putShort((short) getCrc(buff, 0, buff.position()));
+        buff.order(ByteOrder.BIG_ENDIAN);
+        return buff.flip();
+    }
+
+    public class CRCException extends Exception {
+
+        private static final long serialVersionUID = 1L;
+
+        public CRCException(String message) {
+            super(message);
+        }
+
+    }
+
+    private int getCrc(IoBuffer buf, int start, int end) {
+
+        int oldPos = buf.position();
+        buf.position(start);
+
+        if (buf.position() < end) {
+            int crc = 0xFFFF;
+            while (buf.position() < end) {
+                crc ^= (buf.get() & 0xFF);
+                for (int j = 0; j < 8; j++) {
+                    boolean bitOne = ((crc & 0x1) == 0x1);
+                    crc >>>= 1;
+                    if (bitOne) {
+                        crc ^= 0x0000A001;
+                    }
+                }
+            }
+            buf.position(oldPos);
+            return (crc);
+        } else {
+            throw new InvalidParameterException("Invalid start (" + start + ") and end (" + end + ")");
+        }
+    }
+
+    @Override
+    public String toString() {
+        String dataStr = "{";
+        byte[] d = getData();
+        for (int i = 0; i < d.length; i++) {
+            dataStr += (d[i] & 0xFF);
+            if ((i + 1) < d.length) {
+                dataStr += ",";
+            }
+        }
+        dataStr += "}";
+        return "RTUModbusMessage {dev=" + getDevice() + ",func=" + getFunctionCode() + ",data = " + dataStr + " }";
+    }
+
+    public void setFunctionCode(int functionCode) {
+        this.functionCode = functionCode;
+    }
+}
\ No newline at end of file

Propchange: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/rtu/RTUModbusMessage.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/rtu/RTUModbusTransport.java
URL: http://svn.apache.org/viewvc/mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/rtu/RTUModbusTransport.java?rev=1084906&view=auto
==============================================================================
--- mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/rtu/RTUModbusTransport.java (added)
+++ mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/rtu/RTUModbusTransport.java Thu Mar 24 11:35:26 2011
@@ -0,0 +1,103 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.mina.modbus.transport.rtu;
+
+import java.net.InetSocketAddress;
+
+import org.apache.mina.core.RuntimeIoException;
+import org.apache.mina.core.future.ConnectFuture;
+import org.apache.mina.core.service.IoHandler;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.filter.codec.ProtocolDecoder;
+import org.apache.mina.filter.codec.ProtocolEncoder;
+import org.apache.mina.modbus.msg.ModbusMessage;
+import org.apache.mina.modbus.transport.ModbusTransport;
+import org.apache.mina.transport.socket.SocketConnector;
+import org.apache.mina.transport.socket.nio.NioSocketConnector;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class RTUModbusTransport implements ModbusTransport {
+
+    private String hostname;
+
+    private int port;
+
+    private Logger log = LoggerFactory.getLogger(RTUModbusTransport.class);
+
+    public RTUModbusTransport(String hostname, int port) {
+        this.hostname = hostname;
+        this.port = port;
+    }
+
+    public ProtocolDecoder createDecoder(boolean master) {
+        return new RTUModbusDecoder(master);
+    }
+
+    public ProtocolEncoder createEncoder() {
+        return new RTUModbusEncoder();
+    }
+
+    private SocketConnector connector;
+
+    private IoSession session;
+
+    public void connect(final IoHandler handler) {
+        // create connection thread
+        Thread cnxThread = new Thread("ModbusConnection") {
+            @Override
+            public void run() {
+
+                connector = new NioSocketConnector();
+                connector.setHandler(handler);
+                
+                while ((session == null) || !session.isConnected()) {
+                    try {
+                        ConnectFuture future = connector.connect(new InetSocketAddress(hostname, port));
+                        future.awaitUninterruptibly();
+                        session = future.getSession();
+                    } catch (RuntimeIoException e) {
+                        log.error("Exception while connecting : ", e);
+                    }
+
+                    try {
+                        Thread.sleep(2000);
+                    } catch (InterruptedException e1) {
+                        log.error("Exception : ", e1);
+                    }
+                }
+
+            }
+        };
+        cnxThread.start();
+
+    }
+
+    public ModbusMessage createReplyMessage(ModbusMessage query) {
+        RTUModbusMessage msg = (RTUModbusMessage) query;
+        return new RTUModbusMessage(msg.getDevice(), msg.getFunctionCode());
+    }
+
+    public ModbusMessage createMessage(int functionCode, int device) {
+        return new RTUModbusMessage(device, functionCode);
+    }
+
+}
\ No newline at end of file

Propchange: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/rtu/RTUModbusTransport.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/tcp/TCPModbusDecoder.java
URL: http://svn.apache.org/viewvc/mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/tcp/TCPModbusDecoder.java?rev=1084906&view=auto
==============================================================================
--- mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/tcp/TCPModbusDecoder.java (added)
+++ mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/tcp/TCPModbusDecoder.java Thu Mar 24 11:35:26 2011
@@ -0,0 +1,243 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.mina.modbus.transport.tcp;
+
+import java.nio.ByteOrder;
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
+import org.apache.mina.filter.codec.ProtocolDecoderException;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+import org.apache.mina.modbus.ModbusConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TCPModbusDecoder extends CumulativeProtocolDecoder {
+
+    private static final Logger LOG = LoggerFactory.getLogger(TCPModbusDecoder.class);
+
+    private boolean master;
+
+    public TCPModbusDecoder(boolean master) {
+        this.master = master;
+    }
+
+    @Override
+    protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
+
+        in.order(ByteOrder.BIG_ENDIAN);
+
+        int startPos = in.position();
+        boolean isDecoded = false;
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("doDecode bytes : " + in);
+        }
+
+        if ((in.remaining() < 8)) {
+            in.position(startPos);
+            return false;
+        }
+
+        if (master) {
+            isDecoded = decodeReply(in, out);
+        } else {
+            isDecoded = decodeQuery(in, out);
+        }
+
+        if (isDecoded) {
+            return true;
+        }
+
+        in.position(startPos);
+        return false;
+    }
+
+    @SuppressWarnings("unused")
+    private boolean decodeReply(IoBuffer in, ProtocolDecoderOutput out) throws ProtocolDecoderException {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Decode a reply : " + in);
+        }
+        int startPos = in.position();
+
+        in.order(ByteOrder.BIG_ENDIAN);
+
+        // ModBus Application Protocol header
+
+        int transaction = in.getUnsignedShort(startPos + 0);
+        int protocolId = in.getUnsignedShort(startPos + 2);
+        int length = in.getUnsignedShort(startPos + 4);
+        int unitId = in.getUnsigned(startPos + 6);
+
+        // Start of pdu
+        int function = in.getUnsigned(startPos + 7);
+
+        TCPModbusMessage msg;
+
+        // check for Modbus error
+        if ((function & 0x80) > 0) {
+            if (length != 3) {
+                throw new ProtocolDecoderException("Incoherent value with a frame length and PDU length");
+            }
+            int fct = function - 0x80;
+            switch (fct) {
+            case ModbusConstants.READ_HOLDING_REGISTERS:
+            case ModbusConstants.READ_INPUT_REGISTERS:
+            case ModbusConstants.WRITE_SINGLE_REGISTER:
+            case ModbusConstants.WRITE_MULTIPLE_REGISTERS:
+
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Found a modbus error message");
+                }
+
+                msg = new TCPModbusMessage(in);
+                //decode
+                out.write(msg);
+                return true;
+
+            default:
+                throw new ProtocolDecoderException("Unknown data");
+            }
+        }
+
+        switch (function) {
+        case ModbusConstants.READ_HOLDING_REGISTERS:
+        case ModbusConstants.READ_INPUT_REGISTERS:
+
+            if (in.remaining() < 9) {
+                return false;
+            }
+
+            int dataLen = in.getUnsigned(startPos + 8);
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Length (Unit Id + PDU) : " + length + " Byte Count : " + dataLen);
+            }
+            if (length != dataLen + 3) {
+                throw new ProtocolDecoderException("Incoherent value with a frame length and PDU length");
+            }
+
+            if (in.remaining() < dataLen + 9) {
+                return false;
+            }
+
+            msg = new TCPModbusMessage(in);
+            //decode
+            out.write(msg);
+            return true;
+
+        case ModbusConstants.WRITE_SINGLE_REGISTER:
+        case ModbusConstants.WRITE_MULTIPLE_REGISTERS:
+            // fixed len
+            if (length != 6) {
+                throw new ProtocolDecoderException("Incoherent value with a frame length and PDU length");
+            }
+
+            if (in.remaining() < 12) {
+                return false;
+            }
+
+            msg = new TCPModbusMessage(in);
+            //decode
+            out.write(msg);
+            return true;
+
+        default:
+            throw new ProtocolDecoderException("Unknown data");
+        }
+    }
+
+    @SuppressWarnings("unused")
+    private boolean decodeQuery(IoBuffer in, ProtocolDecoderOutput out) throws ProtocolDecoderException {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Decode a query : " + in);
+        }
+        int startPos = in.position();
+
+        in.order(ByteOrder.BIG_ENDIAN);
+
+        // ModBus Application Protocol header
+        int transaction = in.getUnsignedShort(startPos + 0);
+        int protocolId = in.getUnsignedShort(startPos + 2);
+        int length = in.getUnsignedShort(startPos + 4);
+        int unitId = in.getUnsigned(startPos + 6);
+
+        // Start of pdu
+        int function = in.getUnsigned(startPos + 7);
+
+        TCPModbusMessage msg;
+        switch (function) {
+        case ModbusConstants.READ_HOLDING_REGISTERS:
+        case ModbusConstants.READ_INPUT_REGISTERS:
+        case ModbusConstants.WRITE_SINGLE_REGISTER:
+            if (length != 6) {
+                throw new ProtocolDecoderException("Incoherent value with a frame length and PDU length");
+            }
+            // fixed len
+            if (in.remaining() < 12) {
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("no enougth data");
+                }
+                return false;
+            }
+
+            msg = new TCPModbusMessage(in);
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Decoded a Modbus query : " + msg);
+            }
+            out.write(msg);
+
+            return true;
+        case ModbusConstants.WRITE_MULTIPLE_REGISTERS:
+
+            if (in.remaining() < 13) {
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("no enougth data");
+                }
+                return false;
+            }
+
+            int dataLen = in.getUnsigned(startPos + 12);
+
+            if (length != dataLen + 7) {
+                throw new ProtocolDecoderException("Incoherent value with a frame length and PDU length");
+            }
+
+            if (in.remaining() < dataLen + 13) {
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("no enougth data : remaining " + in.remaining() + " was expecting " + (dataLen + 13));
+                }
+                return false;
+            }
+
+            msg = new TCPModbusMessage(in);//, dataLen + 9);
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Decoded a Modbus multiple query : " + msg);
+            }
+            //decode
+            out.write(msg);
+
+            return true;
+        default:
+            throw new ProtocolDecoderException("Unknown data");
+        }
+    }
+
+}

Propchange: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/tcp/TCPModbusDecoder.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/tcp/TCPModbusEncoder.java
URL: http://svn.apache.org/viewvc/mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/tcp/TCPModbusEncoder.java?rev=1084906&view=auto
==============================================================================
--- mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/tcp/TCPModbusEncoder.java (added)
+++ mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/tcp/TCPModbusEncoder.java Thu Mar 24 11:35:26 2011
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.mina.modbus.transport.tcp;
+
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.filter.codec.ProtocolEncoder;
+import org.apache.mina.filter.codec.ProtocolEncoderException;
+import org.apache.mina.filter.codec.ProtocolEncoderOutput;
+
+public class TCPModbusEncoder implements ProtocolEncoder {
+
+    public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws ProtocolEncoderException {
+        if (message instanceof TCPModbusMessage) {
+            IoBuffer buf = ((TCPModbusMessage) message).encode();
+            out.write(buf);
+        } else {
+            throw new ProtocolEncoderException("Unknow message type : " + message.getClass().getName());
+        }
+
+    }
+
+    public void dispose(IoSession session) throws Exception {
+
+    }
+
+}

Propchange: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/tcp/TCPModbusEncoder.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/tcp/TCPModbusMessage.java
URL: http://svn.apache.org/viewvc/mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/tcp/TCPModbusMessage.java?rev=1084906&view=auto
==============================================================================
--- mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/tcp/TCPModbusMessage.java (added)
+++ mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/tcp/TCPModbusMessage.java Thu Mar 24 11:35:26 2011
@@ -0,0 +1,128 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.mina.modbus.transport.tcp;
+
+import java.nio.ByteOrder;
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.apache.mina.modbus.msg.ModbusMessage;
+
+public class TCPModbusMessage implements ModbusMessage {
+
+    // modbus variables
+    private int functionCode;
+
+    public void setFunctionCode(int functionCode) {
+        this.functionCode = functionCode;
+    }
+
+    private byte[] data;
+
+    // TCP Modbus variables
+    private int transactionIdentifier;
+
+    private int protocolIdentifier = 0;
+
+    private int unitId;
+
+    public int getUnitId() {
+        return unitId;
+    }
+
+    public TCPModbusMessage(int transactionIdentifier, int functionCode, int unitId) {
+        this.transactionIdentifier = transactionIdentifier;
+        // this.protocolIdentifier = protocolIdentifier;
+        this.functionCode = functionCode;
+        this.unitId = unitId;
+    }
+
+    public TCPModbusMessage(IoBuffer buffer) {
+        ByteOrder oldOrder = buffer.order();
+        buffer.order(ByteOrder.BIG_ENDIAN);
+
+        transactionIdentifier = buffer.getUnsignedShort();
+        protocolIdentifier = buffer.getUnsignedShort();
+        int length = buffer.getUnsignedShort();
+        unitId = buffer.getUnsigned();
+
+        functionCode = buffer.getUnsigned(); // 1 Byte function code
+        data = new byte[length - 2];
+        buffer.get(data);
+
+        buffer.order(oldOrder);
+    }
+
+    public byte[] getData() {
+        return data;
+    }
+
+    public int getFunctionCode() {
+        return functionCode;
+    }
+
+    public void setData(byte[] data) {
+        this.data = data;
+    }
+
+    public int getTransactionIdentifier() {
+        return transactionIdentifier;
+    }
+
+    public int getProtocolIdentifier() {
+        return protocolIdentifier;
+    }
+
+    /**
+     * create a buffer encoding this PDU
+     * @return a new pooled ByteBuffer
+     */
+    public IoBuffer encode() {
+        IoBuffer buff = IoBuffer.allocate(8 + data.length);
+        buff.order(ByteOrder.BIG_ENDIAN);
+
+        buff.putShort((short) transactionIdentifier);
+        buff.putShort((short) protocolIdentifier);
+        buff.putShort((short) (2 + data.length));
+        buff.put((byte) unitId);
+        buff.put((byte) getFunctionCode());
+        buff.put(data);
+        return buff.flip();
+    }
+
+    @Override
+    public String toString() {
+        String data = "{";
+        byte[] d = getData();
+        for (int i = 0; i < d.length; i++) {
+            data += (d[i] & 0xFF);
+            if ((i + 1) < d.length) {
+                data += ",";
+            }
+        }
+        data += "}";
+        return "TCPModbusMessage {transact=" + getTransactionIdentifier() + ",protocol=" + getProtocolIdentifier()
+                + ",unitId=" + getUnitId() + ",func=" + getFunctionCode() + ",data = " + data + " }";
+    }
+
+    public int getDevice() {
+        return unitId;
+    }
+
+}
\ No newline at end of file

Propchange: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/tcp/TCPModbusMessage.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/tcp/TCPModbusTransport.java
URL: http://svn.apache.org/viewvc/mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/tcp/TCPModbusTransport.java?rev=1084906&view=auto
==============================================================================
--- mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/tcp/TCPModbusTransport.java (added)
+++ mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/tcp/TCPModbusTransport.java Thu Mar 24 11:35:26 2011
@@ -0,0 +1,78 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.mina.modbus.transport.tcp;
+
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+import org.apache.mina.core.service.IoHandler;
+import org.apache.mina.filter.codec.ProtocolDecoder;
+import org.apache.mina.filter.codec.ProtocolEncoder;
+import org.apache.mina.modbus.msg.ModbusMessage;
+import org.apache.mina.modbus.transport.ModbusTransport;
+import org.apache.mina.transport.socket.SocketAcceptor;
+import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TCPModbusTransport implements ModbusTransport {
+
+    private int transactionIdentifier = 0;
+
+    private int port;
+
+    private Logger logger = LoggerFactory.getLogger(TCPModbusTransport.class);
+
+    public TCPModbusTransport(int port) {
+        this.port = port;
+    }
+
+    public void connect(IoHandler handler) {
+        SocketAcceptor acceptor = new NioSocketAcceptor();
+        acceptor.setReuseAddress(true);
+        acceptor.setHandler(handler);
+        try {
+            acceptor.bind(new InetSocketAddress(port));
+        } catch (IOException e) {
+            logger.error("Exception while binding TCP port", e);
+        }
+    }
+
+    public ProtocolDecoder createDecoder(boolean master) {
+        return new TCPModbusDecoder(master);
+    }
+
+    public ProtocolEncoder createEncoder() {
+        return new TCPModbusEncoder();
+    }
+
+    public ModbusMessage createReplyMessage(ModbusMessage query) {
+        TCPModbusMessage msg = (TCPModbusMessage) query;
+        TCPModbusMessage reply = new TCPModbusMessage(msg.getTransactionIdentifier(), msg.getFunctionCode(), msg
+                .getUnitId());
+        return reply;
+    }
+
+    public ModbusMessage createMessage(int functionCode, int unitId) {
+        return new TCPModbusMessage(((int) transactionIdentifier++ % Integer.MAX_VALUE), functionCode, unitId);
+    }
+
+}

Propchange: mina/sandbox/jvermillard/mina-modbus/src/main/java/org/apache/mina/modbus/transport/tcp/TCPModbusTransport.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: mina/sandbox/jvermillard/mina-modbus/src/test/java/org/apache/mina/modbus/transport/rtu/RTUModbusDecoderMasterTest.java
URL: http://svn.apache.org/viewvc/mina/sandbox/jvermillard/mina-modbus/src/test/java/org/apache/mina/modbus/transport/rtu/RTUModbusDecoderMasterTest.java?rev=1084906&view=auto
==============================================================================
--- mina/sandbox/jvermillard/mina-modbus/src/test/java/org/apache/mina/modbus/transport/rtu/RTUModbusDecoderMasterTest.java (added)
+++ mina/sandbox/jvermillard/mina-modbus/src/test/java/org/apache/mina/modbus/transport/rtu/RTUModbusDecoderMasterTest.java Thu Mar 24 11:35:26 2011
@@ -0,0 +1,290 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.mina.modbus.transport.rtu;
+
+import java.util.Arrays;
+import java.util.Random;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.apache.mina.filter.codec.ProtocolCodecSession;
+import org.apache.mina.filter.codec.ProtocolDecoderException;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+import org.apache.mina.modbus.ModbusConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class RTUModbusDecoderMasterTest extends TestCase {
+    RTUModbusDecoder decoder = new RTUModbusDecoder(true);
+    
+    ProtocolCodecSession session = new ProtocolCodecSession();
+    ProtocolDecoderOutput out = session.getDecoderOutput();
+
+    static final Logger LOG = LoggerFactory.getLogger(RTUModbusDecoderMasterTest.class);
+
+    public void testDecodeOneFrame() throws Exception {
+        LOG.info("Decode one frame");
+        // write single register at address 1 and value 50, reply
+        RTUModbusMessage msg = new RTUModbusMessage(1, 6, new byte[] { (byte) 0, (byte) 1, (byte) 0, (byte) 50 });
+        IoBuffer buff = msg.encode();
+
+        // Test one decode and one output
+        IoBuffer in = IoBuffer.allocate(100);
+        in.put(buff); // put
+        in.flip();
+        decoder.decode(session, in, out);
+        Assert.assertEquals(1, session.getDecoderOutputQueue().size());
+    }
+
+    public void testDecodeOneModbusErrorFrame() throws Exception {
+        LOG.info("Decode one frame");
+        // write single register error, reply
+        RTUModbusMessage msg = new RTUModbusMessage(1, 6 + 0x80, new byte[] { (byte) 1 });
+        IoBuffer buff = msg.encode();
+
+        // Test one decode and one output
+        IoBuffer in = IoBuffer.allocate(100);
+        in.put(buff); // put
+        in.flip();
+        decoder.decode(session, in, out);
+        Assert.assertEquals(1, session.getDecoderOutputQueue().size());
+    }
+
+    public void testRejectBuggedFrame() throws Exception {
+        LOG.info("Reject bugged frame");
+        // 4 bytes frame
+        IoBuffer in = IoBuffer.allocate(10);
+        in.putChar('A'); // some crap
+        in.putChar('B');
+        in.flip();
+        //System.err.println("frame size : "+in.remaining());
+        // in.putChar('C');
+        //  in.putChar('x');
+        boolean exception = false;
+        try {
+            decoder.decode(session, in, out);
+        } catch (ProtocolDecoderException e) {
+            exception = true;
+        }
+        Assert.assertTrue(exception);
+        Assert.assertEquals(0, session.getDecoderOutputQueue().size());
+
+        // 2 bytes frame
+        in = IoBuffer.allocate(10);
+        in.put((byte) 0); // 
+        in.put((byte) ModbusConstants.READ_HOLDING_REGISTERS);
+        in.flip();
+        // no exceptions
+        decoder.decode(session, in, out);
+        Assert.assertEquals(0, session.getDecoderOutputQueue().size());
+    }
+
+    public void testRejectCorruptedFrame() throws Exception {
+        LOG.info("Reject corrupted frame");
+        // write single register at address 1 and value 50, reply
+        RTUModbusMessage msg = new RTUModbusMessage(1, 6, new byte[] { (byte) 0, (byte) 1, (byte) 0, (byte) 50 });
+        IoBuffer buff = msg.encode();
+
+        // Test one decode and one output
+        IoBuffer in = IoBuffer.allocate(100);
+        in.put(buff); // put
+        in.put(3, (byte) 56); // some crap
+        in.flip();
+        Exception ex = null;
+        try {
+            decoder.decode(session, in, out);
+        } catch (RTUModbusMessage.CRCException e) {
+            ex = e;
+        }
+        Assert.assertNotNull(ex);
+        Assert.assertEquals(0, session.getDecoderOutputQueue().size());
+
+    }
+
+    public void testAcceptMultiFrame() throws Exception {
+        LOG.info("Accept multi frame");
+        // write single register at address 1 and value 50, reply
+        RTUModbusMessage msg = new RTUModbusMessage(1, 16, new byte[] { (byte) 0, (byte) 1, (byte) 0, (byte) 50 });
+        IoBuffer buff = msg.encode();
+
+        // Test one decode and one output
+        IoBuffer in = IoBuffer.allocate(100);
+        in.put(buff);
+        in.flip();
+        decoder.decode(session, in, out);
+        Assert.assertEquals(1, session.getDecoderOutputQueue().size());
+
+        in = IoBuffer.allocate(100);
+        buff.flip(); // re-put
+        in.put(buff);
+        in.flip();
+        decoder.decode(session, in, out);
+        Assert.assertEquals(2, session.getDecoderOutputQueue().size());
+    }
+
+    public void testFragmentation() throws Exception {
+        LOG.info("Fragmentation");
+
+        // Read multiple register at address 1 and 10 values
+        RTUModbusMessage msgWrite10 = new RTUModbusMessage(1, ModbusConstants.READ_HOLDING_REGISTERS, new byte[] {
+                (byte) 20, (byte) 0, (byte) 1, (byte) 0, (byte) 2, (byte) 0, (byte) 3, (byte) 0, (byte) 4, (byte) 0,
+                (byte) 5, (byte) 0, (byte) 6, (byte) 0, (byte) 7, (byte) 0, (byte) 8, (byte) 0, (byte) 9, (byte) 0,
+                (byte) 10 });
+
+        // Read multiple register at address 1 and 5 values
+        RTUModbusMessage msgWrite5 = new RTUModbusMessage(1, ModbusConstants.READ_HOLDING_REGISTERS, new byte[] {
+                (byte) 10, (byte) 0, (byte) 1, (byte) 0, (byte) 2, (byte) 0, (byte) 3, (byte) 0, (byte) 4, (byte) 0,
+                (byte) 5 });
+
+        // Read multiple register at address 1 and 4 values
+        RTUModbusMessage msgWrite4 = new RTUModbusMessage(1, ModbusConstants.WRITE_MULTIPLE_REGISTERS, new byte[] {
+                (byte) 1, (byte) 0, (byte) 2, (byte) 0 });
+
+        IoBuffer buffPDU1 = msgWrite10.encode();
+        IoBuffer buffPDU2 = msgWrite5.encode();
+        IoBuffer buffPDU3 = msgWrite4.encode();
+        IoBuffer buffPDU123 = IoBuffer.allocate(buffPDU1.remaining() * 2 + buffPDU2.remaining()
+                + buffPDU3.remaining());
+        buffPDU123.put(buffPDU1);
+
+        buffPDU123.put(buffPDU2);
+        buffPDU123.put(buffPDU3);
+        buffPDU1.rewind();
+        buffPDU123.put(buffPDU1);
+        buffPDU123.flip(); // PDU123 = PDU1 + PDU2 + PDU3 + PDU1
+
+        buffPDU1.flip();
+        buffPDU2.flip();
+
+        // dezcode a plain buffer
+        decoder.decode(session, buffPDU1, out);
+        Assert.assertEquals(1, session.getDecoderOutputQueue().size());
+        buffPDU1.rewind();
+
+        IoBuffer buff1 = IoBuffer.allocate(4);
+        while (buff1.hasRemaining()) {
+            buff1.put(buffPDU1.get());
+        }
+
+        buff1.flip();
+        // decode a part of a PDU
+        decoder.decode(session, buff1, out);
+
+        IoBuffer buff2 = IoBuffer.allocate(buffPDU1.remaining());
+        while (buffPDU1.hasRemaining()) {
+            buff2.put(buffPDU1.get());
+        }
+        buff2.flip();
+
+        // decode the second part
+        decoder.decode(session, buff2, out);
+
+        Assert.assertEquals(2, session.getDecoderOutputQueue().size());
+
+        // decode four concatenated big PDU
+        decoder.decode(session, buffPDU123, out);
+        Assert.assertEquals(6, session.getDecoderOutputQueue().size());
+
+        // decode multiple PDU each byte per each byte
+        buffPDU123.rewind();
+        while (buffPDU123.hasRemaining()) {
+            IoBuffer bufOneByte = IoBuffer.allocate(1);
+            bufOneByte.put(buffPDU123.get());
+            bufOneByte.flip();
+            decoder.decode(session, bufOneByte, out);
+        }
+        Assert.assertEquals(10, session.getDecoderOutputQueue().size());
+
+        RTUModbusMessage msg = (RTUModbusMessage) session.getDecoderOutputQueue().poll();
+        Assert.assertTrue(samePDU(msg, msgWrite10));
+        msg = (RTUModbusMessage) session.getDecoderOutputQueue().poll();
+        Assert.assertTrue(samePDU(msg, msgWrite10));
+
+        msg = (RTUModbusMessage) session.getDecoderOutputQueue().poll();
+        Assert.assertTrue(samePDU(msg, msgWrite10));
+        msg = (RTUModbusMessage) session.getDecoderOutputQueue().poll();
+        Assert.assertTrue(samePDU(msg, msgWrite5));
+        msg = (RTUModbusMessage) session.getDecoderOutputQueue().poll();
+        Assert.assertTrue(samePDU(msg, msgWrite4));
+        msg = (RTUModbusMessage) session.getDecoderOutputQueue().poll();
+        Assert.assertTrue(samePDU(msg, msgWrite10));
+        msg = (RTUModbusMessage) session.getDecoderOutputQueue().poll();
+        Assert.assertTrue(samePDU(msg, msgWrite10));
+
+    }
+
+    private boolean samePDU(RTUModbusMessage msg1, RTUModbusMessage msg2) {
+        if (msg1.getDevice() != msg2.getDevice()) {
+            return false;
+        }
+        if (msg1.getFunctionCode() != msg2.getFunctionCode()) {
+            return false;
+        }
+        if (!Arrays.equals(msg1.getData(), msg2.getData())) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public void testRandomPDU() {
+        LOG.info("Random PDU rejection");
+        Random rng = new Random();
+        for (int i = 0; i < 50; i++) {
+
+            IoBuffer buggedBuff = IoBuffer.allocate(8 + rng.nextInt(12));
+
+            while (buggedBuff.remaining() > 0) {
+                buggedBuff.put((byte) rng.nextInt());
+            }
+            buggedBuff.flip();
+            //System.err.println("random buffer of len  : "+buggedBuff.remaining());
+            try {
+                decoder.decode(session, buggedBuff, out);
+            } catch (Exception ex) {
+            }
+            Assert.assertEquals(0, session.getDecoderOutputQueue().size());
+        }
+    }
+
+    public void testByteInsertion() {
+        LOG.info("Insert one byte before PDU");
+        RTUModbusMessage msg = new RTUModbusMessage(1, 6, new byte[] { (byte) 0, (byte) 1, (byte) 0, (byte) 50 });
+        IoBuffer pduBuff = msg.encode();
+
+        IoBuffer buff = IoBuffer.allocate(pduBuff.remaining() + 1);
+        // add a zero before the PCU
+        buff.put((byte) 0);
+        buff.put(pduBuff);
+        buff.flip();
+        boolean exception = false;
+        try {
+            decoder.decode(session, buff, out);
+        } catch (Exception ex) {
+            exception = true;
+        }
+        Assert.assertEquals(0, session.getDecoderOutputQueue().size());
+        Assert.assertTrue(exception);
+    }
+
+}

Propchange: mina/sandbox/jvermillard/mina-modbus/src/test/java/org/apache/mina/modbus/transport/rtu/RTUModbusDecoderMasterTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain