initial commit
This commit is contained in:
		
							
								
								
									
										10
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					*.iml
 | 
				
			||||||
 | 
					.gradle
 | 
				
			||||||
 | 
					/local.properties
 | 
				
			||||||
 | 
					/.idea/libraries
 | 
				
			||||||
 | 
					/.idea/modules.xml
 | 
				
			||||||
 | 
					/.idea/workspace.xml
 | 
				
			||||||
 | 
					.DS_Store
 | 
				
			||||||
 | 
					/build
 | 
				
			||||||
 | 
					/captures
 | 
				
			||||||
 | 
					.externalNativeBuild
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								.idea/caches/build_file_checksums.ser
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								.idea/caches/build_file_checksums.ser
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										29
									
								
								.idea/codeStyles/Project.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								.idea/codeStyles/Project.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					<component name="ProjectCodeStyleConfiguration">
 | 
				
			||||||
 | 
					  <code_scheme name="Project" version="173">
 | 
				
			||||||
 | 
					    <Objective-C-extensions>
 | 
				
			||||||
 | 
					      <file>
 | 
				
			||||||
 | 
					        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
 | 
				
			||||||
 | 
					        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
 | 
				
			||||||
 | 
					        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
 | 
				
			||||||
 | 
					        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
 | 
				
			||||||
 | 
					        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
 | 
				
			||||||
 | 
					        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
 | 
				
			||||||
 | 
					        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
 | 
				
			||||||
 | 
					        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
 | 
				
			||||||
 | 
					        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
 | 
				
			||||||
 | 
					      </file>
 | 
				
			||||||
 | 
					      <class>
 | 
				
			||||||
 | 
					        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
 | 
				
			||||||
 | 
					        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
 | 
				
			||||||
 | 
					        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
 | 
				
			||||||
 | 
					        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
 | 
				
			||||||
 | 
					        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
 | 
				
			||||||
 | 
					        <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
 | 
				
			||||||
 | 
					      </class>
 | 
				
			||||||
 | 
					      <extensions>
 | 
				
			||||||
 | 
					        <pair source="cpp" header="h" fileNamingConvention="NONE" />
 | 
				
			||||||
 | 
					        <pair source="c" header="h" fileNamingConvention="NONE" />
 | 
				
			||||||
 | 
					      </extensions>
 | 
				
			||||||
 | 
					    </Objective-C-extensions>
 | 
				
			||||||
 | 
					  </code_scheme>
 | 
				
			||||||
 | 
					</component>
 | 
				
			||||||
							
								
								
									
										18
									
								
								.idea/gradle.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								.idea/gradle.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<project version="4">
 | 
				
			||||||
 | 
					  <component name="GradleSettings">
 | 
				
			||||||
 | 
					    <option name="linkedExternalProjectsSettings">
 | 
				
			||||||
 | 
					      <GradleProjectSettings>
 | 
				
			||||||
 | 
					        <option name="distributionType" value="DEFAULT_WRAPPED" />
 | 
				
			||||||
 | 
					        <option name="externalProjectPath" value="$PROJECT_DIR$" />
 | 
				
			||||||
 | 
					        <option name="modules">
 | 
				
			||||||
 | 
					          <set>
 | 
				
			||||||
 | 
					            <option value="$PROJECT_DIR$" />
 | 
				
			||||||
 | 
					            <option value="$PROJECT_DIR$/app" />
 | 
				
			||||||
 | 
					          </set>
 | 
				
			||||||
 | 
					        </option>
 | 
				
			||||||
 | 
					        <option name="resolveModulePerSourceSet" value="false" />
 | 
				
			||||||
 | 
					      </GradleProjectSettings>
 | 
				
			||||||
 | 
					    </option>
 | 
				
			||||||
 | 
					  </component>
 | 
				
			||||||
 | 
					</project>
 | 
				
			||||||
							
								
								
									
										34
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<project version="4">
 | 
				
			||||||
 | 
					  <component name="NullableNotNullManager">
 | 
				
			||||||
 | 
					    <option name="myDefaultNullable" value="android.support.annotation.Nullable" />
 | 
				
			||||||
 | 
					    <option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
 | 
				
			||||||
 | 
					    <option name="myNullables">
 | 
				
			||||||
 | 
					      <value>
 | 
				
			||||||
 | 
					        <list size="5">
 | 
				
			||||||
 | 
					          <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
 | 
				
			||||||
 | 
					          <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
 | 
				
			||||||
 | 
					          <item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
 | 
				
			||||||
 | 
					          <item index="3" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
 | 
				
			||||||
 | 
					          <item index="4" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
 | 
				
			||||||
 | 
					        </list>
 | 
				
			||||||
 | 
					      </value>
 | 
				
			||||||
 | 
					    </option>
 | 
				
			||||||
 | 
					    <option name="myNotNulls">
 | 
				
			||||||
 | 
					      <value>
 | 
				
			||||||
 | 
					        <list size="4">
 | 
				
			||||||
 | 
					          <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
 | 
				
			||||||
 | 
					          <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
 | 
				
			||||||
 | 
					          <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
 | 
				
			||||||
 | 
					          <item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
 | 
				
			||||||
 | 
					        </list>
 | 
				
			||||||
 | 
					      </value>
 | 
				
			||||||
 | 
					    </option>
 | 
				
			||||||
 | 
					  </component>
 | 
				
			||||||
 | 
					  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
 | 
				
			||||||
 | 
					    <output url="file://$PROJECT_DIR$/build/classes" />
 | 
				
			||||||
 | 
					  </component>
 | 
				
			||||||
 | 
					  <component name="ProjectType">
 | 
				
			||||||
 | 
					    <option name="id" value="Android" />
 | 
				
			||||||
 | 
					  </component>
 | 
				
			||||||
 | 
					</project>
 | 
				
			||||||
							
								
								
									
										12
									
								
								.idea/runConfigurations.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.idea/runConfigurations.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<project version="4">
 | 
				
			||||||
 | 
					  <component name="RunConfigurationProducerService">
 | 
				
			||||||
 | 
					    <option name="ignoredProducers">
 | 
				
			||||||
 | 
					      <set>
 | 
				
			||||||
 | 
					        <option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
 | 
				
			||||||
 | 
					        <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
 | 
				
			||||||
 | 
					        <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
 | 
				
			||||||
 | 
					      </set>
 | 
				
			||||||
 | 
					    </option>
 | 
				
			||||||
 | 
					  </component>
 | 
				
			||||||
 | 
					</project>
 | 
				
			||||||
							
								
								
									
										6
									
								
								.idea/vcs.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.idea/vcs.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<project version="4">
 | 
				
			||||||
 | 
					  <component name="VcsDirectoryMappings">
 | 
				
			||||||
 | 
					    <mapping directory="$PROJECT_DIR$" vcs="Git" />
 | 
				
			||||||
 | 
					  </component>
 | 
				
			||||||
 | 
					</project>
 | 
				
			||||||
							
								
								
									
										1
									
								
								app/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								app/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					/build
 | 
				
			||||||
							
								
								
									
										30
									
								
								app/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								app/build.gradle
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					apply plugin: 'com.android.application'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					android {
 | 
				
			||||||
 | 
					    compileSdkVersion 27
 | 
				
			||||||
 | 
					    defaultConfig {
 | 
				
			||||||
 | 
					        applicationId "com.example.user.myapp"
 | 
				
			||||||
 | 
					        minSdkVersion 24
 | 
				
			||||||
 | 
					        targetSdkVersion 27
 | 
				
			||||||
 | 
					        versionCode 1
 | 
				
			||||||
 | 
					        versionName "1.0"
 | 
				
			||||||
 | 
					        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    buildTypes {
 | 
				
			||||||
 | 
					        release {
 | 
				
			||||||
 | 
					            minifyEnabled false
 | 
				
			||||||
 | 
					            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					dependencies {
 | 
				
			||||||
 | 
					    implementation fileTree(dir: 'libs', include: ['*.jar'])
 | 
				
			||||||
 | 
					    implementation 'com.android.support:appcompat-v7:27.1.1'
 | 
				
			||||||
 | 
					    implementation 'com.android.support.constraint:constraint-layout:1.1.0'
 | 
				
			||||||
 | 
					    testImplementation 'junit:junit:4.12'
 | 
				
			||||||
 | 
					    androidTestImplementation 'com.android.support.test:runner:1.0.2'
 | 
				
			||||||
 | 
					    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
 | 
				
			||||||
 | 
					    implementation 'org.apache.commons:commons-lang3:3.7'
 | 
				
			||||||
 | 
					    implementation group: 'org.slf4j', name: 'slf4j-android', version: '1.7.7'
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										21
									
								
								app/proguard-rules.pro
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								app/proguard-rules.pro
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					# Add project specific ProGuard rules here.
 | 
				
			||||||
 | 
					# You can control the set of applied configuration files using the
 | 
				
			||||||
 | 
					# proguardFiles setting in build.gradle.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# For more details, see
 | 
				
			||||||
 | 
					#   http://developer.android.com/guide/developing/tools/proguard.html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# If your project uses WebView with JS, uncomment the following
 | 
				
			||||||
 | 
					# and specify the fully qualified class name to the JavaScript interface
 | 
				
			||||||
 | 
					# class:
 | 
				
			||||||
 | 
					#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
 | 
				
			||||||
 | 
					#   public *;
 | 
				
			||||||
 | 
					#}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Uncomment this to preserve the line number information for
 | 
				
			||||||
 | 
					# debugging stack traces.
 | 
				
			||||||
 | 
					#-keepattributes SourceFile,LineNumberTable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# If you keep the line number information, uncomment this to
 | 
				
			||||||
 | 
					# hide the original source file name.
 | 
				
			||||||
 | 
					#-renamesourcefileattribute SourceFile
 | 
				
			||||||
@@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					package com.example.user.myapp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.content.Context;
 | 
				
			||||||
 | 
					import android.support.test.InstrumentationRegistry;
 | 
				
			||||||
 | 
					import android.support.test.runner.AndroidJUnit4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.junit.Test;
 | 
				
			||||||
 | 
					import org.junit.runner.RunWith;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static org.junit.Assert.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Instrumented test, which will execute on an Android device.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@RunWith(AndroidJUnit4.class)
 | 
				
			||||||
 | 
					public class ExampleInstrumentedTest {
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void useAppContext() {
 | 
				
			||||||
 | 
					        // Context of the app under test.
 | 
				
			||||||
 | 
					        Context appContext = InstrumentationRegistry.getTargetContext();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertEquals("com.example.user.myapp", appContext.getPackageName());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										40
									
								
								app/src/main/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								app/src/main/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    package="com.example.user.myapp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <uses-permission android:name="android.permission.INTERNET" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <application
 | 
				
			||||||
 | 
					        android:name=".GlobalState"
 | 
				
			||||||
 | 
					        android:allowBackup="true"
 | 
				
			||||||
 | 
					        android:icon="@mipmap/ic_launcher"
 | 
				
			||||||
 | 
					        android:label="@string/app_name"
 | 
				
			||||||
 | 
					        android:roundIcon="@mipmap/ic_launcher_round"
 | 
				
			||||||
 | 
					        android:supportsRtl="true"
 | 
				
			||||||
 | 
					        android:theme="@style/AppTheme">
 | 
				
			||||||
 | 
					        <activity android:name=".MainActivity">
 | 
				
			||||||
 | 
					            <intent-filter>
 | 
				
			||||||
 | 
					                <action android:name="android.intent.action.MAIN" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <category android:name="android.intent.category.LAUNCHER" />
 | 
				
			||||||
 | 
					            </intent-filter>
 | 
				
			||||||
 | 
					        </activity>
 | 
				
			||||||
 | 
					        <activity
 | 
				
			||||||
 | 
					            android:name=".DisplayMessageActivity"
 | 
				
			||||||
 | 
					            android:parentActivityName=".MainActivity">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- The meta-data tag is required if you support API level 15 and lower -->
 | 
				
			||||||
 | 
					            <meta-data
 | 
				
			||||||
 | 
					                android:name="android.support.PARENT_ACTIVITY"
 | 
				
			||||||
 | 
					                android:value=".MainActivity" />
 | 
				
			||||||
 | 
					        </activity>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <service
 | 
				
			||||||
 | 
					            android:name=".MyIntentService"
 | 
				
			||||||
 | 
					            android:exported="false" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <activity android:name=".MeasurementActivity" />
 | 
				
			||||||
 | 
					        <activity android:name=".ManualDriveActivity"></activity>
 | 
				
			||||||
 | 
					    </application>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</manifest>
 | 
				
			||||||
@@ -0,0 +1,114 @@
 | 
				
			|||||||
 | 
					package com.example.user.myapp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.content.BroadcastReceiver;
 | 
				
			||||||
 | 
					import android.content.Context;
 | 
				
			||||||
 | 
					import android.content.Intent;
 | 
				
			||||||
 | 
					import android.content.IntentFilter;
 | 
				
			||||||
 | 
					import android.os.Handler;
 | 
				
			||||||
 | 
					import android.support.v4.content.LocalBroadcastManager;
 | 
				
			||||||
 | 
					import android.support.v7.app.AppCompatActivity;
 | 
				
			||||||
 | 
					import android.os.Bundle;
 | 
				
			||||||
 | 
					import android.widget.TextView;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Calendar;
 | 
				
			||||||
 | 
					import java.util.Date;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class DisplayMessageActivity extends AppCompatActivity {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private Handler handlerCoil;
 | 
				
			||||||
 | 
					    private GlobalState state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected void onCreate(Bundle savedInstanceState) {
 | 
				
			||||||
 | 
					        super.onCreate(savedInstanceState);
 | 
				
			||||||
 | 
					        setContentView(R.layout.activity_display_message);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver, new IntentFilter("readCoil8192"));
 | 
				
			||||||
 | 
					        state = (GlobalState) getApplicationContext();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        handlerCoil = new Handler();
 | 
				
			||||||
 | 
					        handlerCoil.post(refreshCoil);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Runnable refreshCoil = new Runnable() {
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void run() {
 | 
				
			||||||
 | 
					        Intent serviceIntent = new Intent(getApplicationContext(), MyIntentService.class);
 | 
				
			||||||
 | 
					        serviceIntent.setAction("read.coil");
 | 
				
			||||||
 | 
					        serviceIntent.putExtra("extra.ip.address", state.getIpAddress());
 | 
				
			||||||
 | 
					        serviceIntent.putExtra("extra.ip.port", state.getPort());
 | 
				
			||||||
 | 
					        serviceIntent.putExtra("extra.ref", state.getCoilRef());
 | 
				
			||||||
 | 
					        serviceIntent.putExtra("extra.count", state.getCoilCount());
 | 
				
			||||||
 | 
					        serviceIntent.putExtra("extra.intent.name", "readCoil8192");
 | 
				
			||||||
 | 
					        getApplicationContext().startService(serviceIntent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        handlerCoil.postDelayed(this, 2000);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Runnable refreshDiscretInput = new Runnable() {
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void run() {
 | 
				
			||||||
 | 
					            // TODO Auto-generated method stub
 | 
				
			||||||
 | 
					            TextView textView = findViewById(R.id.textView1);
 | 
				
			||||||
 | 
					            Date currentTime = Calendar.getInstance().getTime();
 | 
				
			||||||
 | 
					            textView.setText(new Double(Math.random()).toString());
 | 
				
			||||||
 | 
					            handlerCoil.postDelayed(this, 1000);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Runnable refreshInputRegister = new Runnable() {
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void run() {
 | 
				
			||||||
 | 
					            // TODO Auto-generated method stub
 | 
				
			||||||
 | 
					            TextView textView = findViewById(R.id.textView1);
 | 
				
			||||||
 | 
					            Date currentTime = Calendar.getInstance().getTime();
 | 
				
			||||||
 | 
					            textView.setText(new Double(Math.random()).toString());
 | 
				
			||||||
 | 
					            handlerCoil.postDelayed(this, 1000);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Runnable refreshHoldingRegister = new Runnable() {
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void run() {
 | 
				
			||||||
 | 
					            // TODO Auto-generated method stub
 | 
				
			||||||
 | 
					            TextView textView = findViewById(R.id.textView1);
 | 
				
			||||||
 | 
					            Date currentTime = Calendar.getInstance().getTime();
 | 
				
			||||||
 | 
					            textView.setText(new Double(Math.random()).toString());
 | 
				
			||||||
 | 
					            handlerCoil.postDelayed(this, 1000);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onReceive(Context context, Intent intent) {
 | 
				
			||||||
 | 
					            String action = intent.getAction();
 | 
				
			||||||
 | 
					            Bundle bundle = intent.getExtras();
 | 
				
			||||||
 | 
					            boolean[] booleanArray = bundle.getBooleanArray("values");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            TextView textView1 = findViewById(R.id.textView1);
 | 
				
			||||||
 | 
					            textView1.setText(new Boolean(booleanArray[0]).toString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            TextView textView2 = findViewById(R.id.textView2);
 | 
				
			||||||
 | 
					            textView2.setText(new Boolean(booleanArray[1]).toString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            TextView textView3 = findViewById(R.id.textView3);
 | 
				
			||||||
 | 
					            textView3.setText(new Boolean(booleanArray[2]).toString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            TextView textView4 = findViewById(R.id.textView4);
 | 
				
			||||||
 | 
					            textView4.setText(new Boolean(booleanArray[3]).toString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            TextView textView5 = findViewById(R.id.textView5);
 | 
				
			||||||
 | 
					            textView5.setText(new Boolean(booleanArray[4]).toString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            TextView textView6 = findViewById(R.id.textView6);
 | 
				
			||||||
 | 
					            textView6.setText(new Boolean(booleanArray[5]).toString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            TextView textView7 = findViewById(R.id.textView7);
 | 
				
			||||||
 | 
					            textView7.setText(new Boolean(booleanArray[6]).toString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            TextView textView8 = findViewById(R.id.textView8);
 | 
				
			||||||
 | 
					            textView8.setText(new Boolean(booleanArray[7]).toString());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										51
									
								
								app/src/main/java/com/example/user/myapp/GlobalState.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								app/src/main/java/com/example/user/myapp/GlobalState.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					package com.example.user.myapp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.app.Application;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.facade.ModbusTCPMaster;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.TCPMasterConnection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.net.InetAddress;
 | 
				
			||||||
 | 
					import java.net.UnknownHostException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class GlobalState extends Application {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // coil
 | 
				
			||||||
 | 
					    private String ipAddress;
 | 
				
			||||||
 | 
					    private int port = 502;
 | 
				
			||||||
 | 
					    private int coilRef;
 | 
				
			||||||
 | 
					    private int coilCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // getter/setter
 | 
				
			||||||
 | 
					    public String getIpAddress() {
 | 
				
			||||||
 | 
					        return ipAddress;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setIpAddress(String ipAddress) {
 | 
				
			||||||
 | 
					        this.ipAddress = ipAddress;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int getPort() {
 | 
				
			||||||
 | 
					        return port;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setPort(int port) {
 | 
				
			||||||
 | 
					        this.port = port;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int getCoilCount() {
 | 
				
			||||||
 | 
					        return coilCount;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setCoilCount(int coilCount) {
 | 
				
			||||||
 | 
					        this.coilCount = coilCount;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setCoilRef(int coilRef) {
 | 
				
			||||||
 | 
					        this.coilRef = coilRef;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int getCoilRef() {
 | 
				
			||||||
 | 
					        return coilRef;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										52
									
								
								app/src/main/java/com/example/user/myapp/MainActivity.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								app/src/main/java/com/example/user/myapp/MainActivity.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
				
			|||||||
 | 
					package com.example.user.myapp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.content.Intent;
 | 
				
			||||||
 | 
					import android.support.v7.app.AppCompatActivity;
 | 
				
			||||||
 | 
					import android.os.Bundle;
 | 
				
			||||||
 | 
					import android.view.View;
 | 
				
			||||||
 | 
					import android.widget.EditText;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class MainActivity extends AppCompatActivity {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected void onCreate(Bundle savedInstanceState) {
 | 
				
			||||||
 | 
					        super.onCreate(savedInstanceState);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setContentView(R.layout.activity_main);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Called when the user taps the Send button */
 | 
				
			||||||
 | 
					    public void sendMessage(View view) {
 | 
				
			||||||
 | 
					        EditText editText1 = (EditText) findViewById(R.id.ipAddress);
 | 
				
			||||||
 | 
					        String ipAddress = editText1.getText().toString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        EditText editText2 = (EditText) findViewById(R.id.port);
 | 
				
			||||||
 | 
					        int port = Integer.parseInt(editText2.getText().toString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        EditText editText3 = (EditText) findViewById(R.id.ref);
 | 
				
			||||||
 | 
					        int ref = Integer.parseInt(editText3.getText().toString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        EditText editText4 = (EditText) findViewById(R.id.count);
 | 
				
			||||||
 | 
					        int count = Integer.parseInt(editText4.getText().toString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        GlobalState state = (GlobalState) getApplicationContext();
 | 
				
			||||||
 | 
					        state.setIpAddress(ipAddress);
 | 
				
			||||||
 | 
					        state.setPort(port);
 | 
				
			||||||
 | 
					        state.setCoilRef(ref);
 | 
				
			||||||
 | 
					        state.setCoilCount(count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Intent serviceIntent = new Intent(this, MyIntentService.class);
 | 
				
			||||||
 | 
					        serviceIntent.setAction("read.coil");
 | 
				
			||||||
 | 
					        serviceIntent.putExtra("extra.ip.address", ipAddress);
 | 
				
			||||||
 | 
					        serviceIntent.putExtra("extra.ip.port", port);
 | 
				
			||||||
 | 
					        serviceIntent.putExtra("extra.ref", ref);
 | 
				
			||||||
 | 
					        serviceIntent.putExtra("extra.count", count);
 | 
				
			||||||
 | 
					        serviceIntent.putExtra("extra.intent.name", "readCoil8192");
 | 
				
			||||||
 | 
					        this.startService(serviceIntent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Intent nextIntent = new Intent(this, DisplayMessageActivity.class);
 | 
				
			||||||
 | 
					        this.startActivity(nextIntent);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					package com.example.user.myapp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.support.v7.app.AppCompatActivity;
 | 
				
			||||||
 | 
					import android.os.Bundle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ManualDriveActivity extends AppCompatActivity {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected void onCreate(Bundle savedInstanceState) {
 | 
				
			||||||
 | 
					        super.onCreate(savedInstanceState);
 | 
				
			||||||
 | 
					        setContentView(R.layout.activity_manual_drive);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					package com.example.user.myapp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.support.v7.app.AppCompatActivity;
 | 
				
			||||||
 | 
					import android.os.Bundle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class MeasurementActivity extends AppCompatActivity {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Boolean myBoolean = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected void onCreate(Bundle savedInstanceState) {
 | 
				
			||||||
 | 
					        super.onCreate(savedInstanceState);
 | 
				
			||||||
 | 
					        setContentView(R.layout.activity_measurement);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean toggle () {
 | 
				
			||||||
 | 
					        return myBoolean ? false: true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										115
									
								
								app/src/main/java/com/example/user/myapp/MyIntentService.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								app/src/main/java/com/example/user/myapp/MyIntentService.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,115 @@
 | 
				
			|||||||
 | 
					package com.example.user.myapp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.app.IntentService;
 | 
				
			||||||
 | 
					import android.content.Intent;
 | 
				
			||||||
 | 
					import android.content.Context;
 | 
				
			||||||
 | 
					import android.os.Bundle;
 | 
				
			||||||
 | 
					import android.support.v4.content.LocalBroadcastManager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.ModbusException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.facade.ModbusTCPMaster;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.io.ModbusTransaction;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ModbusResponse;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.TCPMasterConnection;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.util.BitVector;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.net.UnknownHostException;
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * An {@link IntentService} subclass for handling asynchronous task requests in
 | 
				
			||||||
 | 
					 * a service on a separate handler thread.
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * TODO: Customize class - update intent actions, extra parameters and static
 | 
				
			||||||
 | 
					 * helper methods.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class MyIntentService extends IntentService {
 | 
				
			||||||
 | 
					    // TODO: Rename actions, choose action names that describe tasks that this
 | 
				
			||||||
 | 
					    // IntentService can perform, e.g. ACTION_FETCH_NEW_ITEMS
 | 
				
			||||||
 | 
					  //  private static final String ACTION_FOO = "com.example.user.myapp.action.FOO";
 | 
				
			||||||
 | 
					    private static final String ACTION_READ_COIL = "read.coil";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private ModbusTCPMaster master;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // TODO: Rename parameters
 | 
				
			||||||
 | 
					    private static final String EXTRA_IP_ADDRESS = "extra.ip.address";
 | 
				
			||||||
 | 
					    private static final String EXTRA_PORT = "extra.ip.port";
 | 
				
			||||||
 | 
					    private static final String EXTRA_REF = "extra.ref";
 | 
				
			||||||
 | 
					    private static final String EXTRA_COUNT = "extra.count";
 | 
				
			||||||
 | 
					    private static final String EXTRA_INTENT_NAME = "extra.intent.name";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 //   private static final String EXTRA_PARAM2 = "com.example.user.myapp.extra.PARAM2";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public MyIntentService() {
 | 
				
			||||||
 | 
					        super("MyIntentService");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void sendBooleanListToActivity(List<Boolean> booleanList, String intentName) {
 | 
				
			||||||
 | 
					        Intent intent = new Intent(intentName);
 | 
				
			||||||
 | 
					        Bundle bundle = new Bundle();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bundle.putBooleanArray("values", toPrimitiveArray(booleanList));
 | 
				
			||||||
 | 
					        intent.putExtras(bundle);
 | 
				
			||||||
 | 
					        sendLocationBroadcast(intent);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void sendLocationBroadcast(Intent intent) {
 | 
				
			||||||
 | 
					        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readCoilAction (Context context, String ipAddress, int port, int ref, int count, String intentReceiveName) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            master = new ModbusTCPMaster(ipAddress, port);
 | 
				
			||||||
 | 
					            master.connect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            BitVector bv = master.readCoils(ref,count);
 | 
				
			||||||
 | 
					            List<Boolean> listBooleanBit = new ArrayList<>();
 | 
				
			||||||
 | 
					            for (int i = 0; i < count; i++) {
 | 
				
			||||||
 | 
					                listBooleanBit.add(bv.getBit(i));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            sendBooleanListToActivity(listBooleanBit, intentReceiveName);
 | 
				
			||||||
 | 
					            master.disconnect();
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            System.out.println("Exception in reading coil " + e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected void onHandleIntent(Intent intent) {
 | 
				
			||||||
 | 
					        GlobalState state = (GlobalState) getApplicationContext();
 | 
				
			||||||
 | 
					        if (intent != null) {
 | 
				
			||||||
 | 
					            final String action = intent.getAction();
 | 
				
			||||||
 | 
					            final String ipAddress = intent.getStringExtra(EXTRA_IP_ADDRESS);
 | 
				
			||||||
 | 
					            final int port = intent.getIntExtra(EXTRA_PORT, 502);
 | 
				
			||||||
 | 
					            final int ref = intent.getIntExtra(EXTRA_REF, 8192);
 | 
				
			||||||
 | 
					            final int count = intent.getIntExtra(EXTRA_COUNT, 8);
 | 
				
			||||||
 | 
					            final String intentName = intent.getStringExtra(EXTRA_INTENT_NAME);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            switch(action) {
 | 
				
			||||||
 | 
					                //case ACTION_FOO :
 | 
				
			||||||
 | 
					                //    handleActionFoo(param1, param2);
 | 
				
			||||||
 | 
					                //   break;
 | 
				
			||||||
 | 
					                case ACTION_READ_COIL :
 | 
				
			||||||
 | 
					                    handleReadCoilAction(ipAddress, port, ref, count, intentName);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Handle action Baz in the provided background thread with the provided
 | 
				
			||||||
 | 
					     * parameters.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void handleReadCoilAction(String ipAddress, int port, int ref, int count, String intentName) {
 | 
				
			||||||
 | 
					        readCoilAction(this, ipAddress, port, ref, count, intentName);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private boolean[] toPrimitiveArray(final List<Boolean> booleanList) {
 | 
				
			||||||
 | 
					        final boolean[] primitives = new boolean[booleanList.size()];
 | 
				
			||||||
 | 
					        int index = 0;
 | 
				
			||||||
 | 
					        for (Boolean object : booleanList) {
 | 
				
			||||||
 | 
					            primitives[index++] = object;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return primitives;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										89
									
								
								app/src/main/java/com/example/user/myapp/MyTest.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								app/src/main/java/com/example/user/myapp/MyTest.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,89 @@
 | 
				
			|||||||
 | 
					package com.example.user.myapp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.app.Activity;
 | 
				
			||||||
 | 
					import android.content.Context;
 | 
				
			||||||
 | 
					import android.content.Intent;
 | 
				
			||||||
 | 
					import android.os.AsyncTask;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.ModbusException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.facade.ModbusTCPMaster;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.io.ModbusTCPTransaction;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.io.ModbusTransaction;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ModbusResponse;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ReadCoilsRequest;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ReadCoilsResponse;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.TCPMasterConnection;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.util.BitVector;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.net.InetAddress;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class MyTest extends AsyncTask<String, Void, String> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  //  private ModbusTCPMaster master = null;
 | 
				
			||||||
 | 
					    TCPMasterConnection connection = null;
 | 
				
			||||||
 | 
					    Activity prevActivityContext = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public MyTest (Activity context, TCPMasterConnection connection) {
 | 
				
			||||||
 | 
					        this.connection = connection;
 | 
				
			||||||
 | 
					        this.prevActivityContext = context;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected String doInBackground(String...strings) {
 | 
				
			||||||
 | 
					        System.out.println("Pass !! ");
 | 
				
			||||||
 | 
					        boolean open = false;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					          //  master = new ModbusTCPMaster(strings[0], 503);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         //   InetAddress inetAddress = InetAddress.getByName("192.168.157.16");
 | 
				
			||||||
 | 
					        //    TCPMasterConnection connection = new TCPMasterConnection(inetAddress);
 | 
				
			||||||
 | 
					        //    connection.setPort(503);
 | 
				
			||||||
 | 
					            connection.connect();
 | 
				
			||||||
 | 
					            open = connection.isConnected();
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					            System.out.print("am i open : "+open);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ReadCoilsRequest request = new ReadCoilsRequest(8192, 8);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ModbusTransaction transaction = new ModbusTCPTransaction(connection);
 | 
				
			||||||
 | 
					            transaction.setRequest(request);
 | 
				
			||||||
 | 
					            transaction.execute();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            BitVector bv = ((ReadCoilsResponse) getAndCheckResponse(transaction)).getCoils();
 | 
				
			||||||
 | 
					              // bv.forceSize(count);
 | 
				
			||||||
 | 
					            System.out.println("my bit  :"+bv.getBit(0));
 | 
				
			||||||
 | 
					            connection.close();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          //  this.master.connect();
 | 
				
			||||||
 | 
					       //     master.connect();
 | 
				
			||||||
 | 
					       //     return null;
 | 
				
			||||||
 | 
					            return "i'm connected";
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            return e.getMessage();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected void onPostExecute(String string) {
 | 
				
			||||||
 | 
					        //     //   try {
 | 
				
			||||||
 | 
					            System.out.println("finished");
 | 
				
			||||||
 | 
					            System.out.println(string);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Intent intent = new Intent(this.prevActivityContext, DisplayMessageActivity.class);
 | 
				
			||||||
 | 
					       //     intent.putExtra("connection", (Object) this.master);
 | 
				
			||||||
 | 
					         //   System.out.println (master);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            prevActivityContext.startActivity(intent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private ModbusResponse getAndCheckResponse(ModbusTransaction transaction) throws ModbusException {
 | 
				
			||||||
 | 
					        ModbusResponse res = transaction.getResponse();
 | 
				
			||||||
 | 
					        if (res == null) {
 | 
				
			||||||
 | 
					            throw new ModbusException("No response");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return res;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										349
									
								
								app/src/main/java/com/ghgande/j2mod/modbus/Modbus.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										349
									
								
								app/src/main/java/com/ghgande/j2mod/modbus/Modbus.java
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,349 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Interface defining all constants related to the
 | 
				
			||||||
 | 
					 * Modbus protocol.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @version 1.2rc1 (09/11/2004)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public interface Modbus {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the class 1 function code
 | 
				
			||||||
 | 
					     * for <tt>read coils</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int READ_COILS = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines a class 1 function code
 | 
				
			||||||
 | 
					     * for <tt>read input discretes</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int READ_INPUT_DISCRETES = 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines a class 1 function code
 | 
				
			||||||
 | 
					     * for <tt>read holding registers</tt>
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int READ_HOLDING_REGISTERS = 3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the class 0 function code
 | 
				
			||||||
 | 
					     * for <tt>read multiple registers</tt>.  The
 | 
				
			||||||
 | 
					     * proper name is "Read Holding Registers".
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int READ_MULTIPLE_REGISTERS = 3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines a class 1 function code
 | 
				
			||||||
 | 
					     * for <tt>read input registers</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int READ_INPUT_REGISTERS = 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines a class 1 function code
 | 
				
			||||||
 | 
					     * for <tt>write coil</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int WRITE_COIL = 5;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines a class 1 function code
 | 
				
			||||||
 | 
					     * for <tt>write single register</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int WRITE_SINGLE_REGISTER = 6;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * <tt>read exception status</tt>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Serial devices only.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int READ_EXCEPTION_STATUS = 7;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * <tt>get serial diagnostics</tt>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Serial devices only.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int READ_SERIAL_DIAGNOSTICS = 8;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * <tt>get comm event counter</tt>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Serial devices only.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int READ_COMM_EVENT_COUNTER = 11;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * <tt>get comm event log</tt>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Serial devices only.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int READ_COMM_EVENT_LOG = 12;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines a standard function code
 | 
				
			||||||
 | 
					     * for <tt>write multiple coils</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int WRITE_MULTIPLE_COILS = 15;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the class 0 function code
 | 
				
			||||||
 | 
					     * for <tt>write multiple registers</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int WRITE_MULTIPLE_REGISTERS = 16;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines a standard function code
 | 
				
			||||||
 | 
					     * for <tt>read slave ID</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int REPORT_SLAVE_ID = 17;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * <tt>read file record</tt>
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int READ_FILE_RECORD = 20;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * <tt>write file record</tt>
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int WRITE_FILE_RECORD = 21;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * <tt>mask write register</tt>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Update a single register using its current value and an AND
 | 
				
			||||||
 | 
					     * and OR mask.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int MASK_WRITE_REGISTER = 22;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * <tt>read / write multiple registers</tt>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Write some number of registers, then read some number of
 | 
				
			||||||
 | 
					     * potentially other registers back.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int READ_WRITE_MULTIPLE = 23;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * <tt>read FIFO queue</tt>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Read from a FIFO queue.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int READ_FIFO_QUEUE = 24;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the function code for reading
 | 
				
			||||||
 | 
					     * encapsulated data, such as vendor information.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int READ_MEI = 43;
 | 
				
			||||||
 | 
					    int READ_MEI_VENDOR_INFO = 14;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the byte representation of the coil state <b>on</b>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int COIL_ON = (byte)255;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the byte representation of the coil state <b>pos</b>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int COIL_OFF = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the word representation of the coil state <b>on</b>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    byte[] COIL_ON_BYTES = {(byte)COIL_ON, (byte)COIL_OFF};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the word representation of the coil state <b>pos</b>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    byte[] COIL_OFF_BYTES = {(byte)COIL_OFF, (byte)COIL_OFF};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the maximum number of bits in multiple read/write
 | 
				
			||||||
 | 
					     * of input discretes or coils (<b>2000</b>).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int MAX_BITS = 2000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the Modbus slave exception offset that is added to the
 | 
				
			||||||
 | 
					     * function code, to flag an exception.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int EXCEPTION_OFFSET = 128;            //the last valid function code is 127
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the Modbus slave exception type <tt>illegal function</tt>.
 | 
				
			||||||
 | 
					     * This exception code is returned if the slave:
 | 
				
			||||||
 | 
					     * <ul>
 | 
				
			||||||
 | 
					     * <li>does not implement the function code <b>or</b></li>
 | 
				
			||||||
 | 
					     * <li>is not in a state that allows it to process the function</li>
 | 
				
			||||||
 | 
					     * </ul>
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int ILLEGAL_FUNCTION_EXCEPTION = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the Modbus slave exception type <tt>illegal data address</tt>.
 | 
				
			||||||
 | 
					     * This exception code is returned if the reference:
 | 
				
			||||||
 | 
					     * <ul>
 | 
				
			||||||
 | 
					     * <li>does not exist on the slave <b>or</b></li>
 | 
				
			||||||
 | 
					     * <li>the combination of reference and length exceeds the bounds
 | 
				
			||||||
 | 
					     * of the existing registers.
 | 
				
			||||||
 | 
					     * </li>
 | 
				
			||||||
 | 
					     * </ul>
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int ILLEGAL_ADDRESS_EXCEPTION = 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the Modbus slave exception type <tt>illegal data value</tt>.
 | 
				
			||||||
 | 
					     * This exception code indicates a fault in the structure of the data values
 | 
				
			||||||
 | 
					     * of a complex request, such as an incorrect implied length.<br>
 | 
				
			||||||
 | 
					     * <b>This code does not indicate a problem with application specific validity
 | 
				
			||||||
 | 
					     * of the value.</b>
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int ILLEGAL_VALUE_EXCEPTION = 3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the Modbus slave exception type <tt>slave device failure</tt>.
 | 
				
			||||||
 | 
					     * This exception code indicates a fault in the slave device itself.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int SLAVE_DEVICE_FAILURE = 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the Modbus slave exception type <tt>slave busy</tt>.  This
 | 
				
			||||||
 | 
					     * exception indicates the the slave is unable to perform the operation
 | 
				
			||||||
 | 
					     * because it is performing an operation which cannot be interrupted.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int SLAVE_BUSY_EXCEPTION = 6;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the Modbus slave exception type <tt>negative acknowledgment</tt>.
 | 
				
			||||||
 | 
					     * This exception code indicates the slave cannot perform the requested
 | 
				
			||||||
 | 
					     * action.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int NEGATIVE_ACKNOWLEDGEMENT = 7;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the Modbus slave exception type <tt>Gateway target failed to
 | 
				
			||||||
 | 
					     * respond</tt>.  This exception code indicates that a Modbus gateway
 | 
				
			||||||
 | 
					     * failed to receive a response from the specified target.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int GATEWAY_TARGET_NO_RESPONSE = 11;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the default port number of Modbus
 | 
				
			||||||
 | 
					     * (=<tt>502</tt>).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int DEFAULT_PORT = 502;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the maximum message length in bytes
 | 
				
			||||||
 | 
					     * (=<tt>256</tt>).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int MAX_MESSAGE_LENGTH = 256;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the default transaction identifier (=<tt>0</tt>).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int DEFAULT_TRANSACTION_ID = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the default protocol identifier (=<tt>0</tt>).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int DEFAULT_PROTOCOL_ID = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the default unit identifier (=<tt>0</tt>).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int DEFAULT_UNIT_ID = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the default setting for validity checking
 | 
				
			||||||
 | 
					     * in transactions (=<tt>true</tt>).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    boolean DEFAULT_VALIDITYCHECK = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the default setting for I/O operation timeouts
 | 
				
			||||||
 | 
					     * in milliseconds (=<tt>3000</tt>).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int DEFAULT_TIMEOUT = 3000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the sleep period between transaction retries
 | 
				
			||||||
 | 
					     * in milliseconds (=<tt>200</tt>).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int RETRY_SLEEP_TIME = 500;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the default reconnecting setting for
 | 
				
			||||||
 | 
					     * transactions (=<tt>false</tt>).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    boolean DEFAULT_RECONNECTING = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the default amount of retires for opening
 | 
				
			||||||
 | 
					     * a connection (=<tt>3</tt>).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int DEFAULT_RETRIES = 5;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the default number of msec to delay before transmission<br>
 | 
				
			||||||
 | 
					     * Inter-message delays are managed by the SerialTransaction object automatically based on the
 | 
				
			||||||
 | 
					     * baud rate. Setting this value to anything other than zero will bypass that process and force
 | 
				
			||||||
 | 
					     * a specific inter-message delay
 | 
				
			||||||
 | 
					     * (=<tt>0</tt>).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int DEFAULT_TRANSMIT_DELAY = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the default number of msec to delay before transmission if not overridden by DEFAULT_TRANSMIT_DELAY
 | 
				
			||||||
 | 
					     * (=<tt>2</tt>).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int MINIMUM_TRANSMIT_DELAY = 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The number of characters delay that must be maintained between adjacent requests on
 | 
				
			||||||
 | 
					     * the same serial port (within the same transaction)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    double INTER_MESSAGE_GAP = 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the maximum value of the transaction identifier.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * <p><b>Note:</b> The standard requires that the server copy whatever
 | 
				
			||||||
 | 
					     * value the client provides. However, the transaction ID is being
 | 
				
			||||||
 | 
					     * limited to signed 16-bit integers to prevent problems with servers
 | 
				
			||||||
 | 
					     * that might incorrectly assume the value is a signed value.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int MAX_TRANSACTION_ID = Short.MAX_VALUE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the serial encoding "ASCII".
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String SERIAL_ENCODING_ASCII = "ascii";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the serial encoding "RTU".
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String SERIAL_ENCODING_RTU = "rtu";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the default serial encoding (ASCII).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String DEFAULT_SERIAL_ENCODING = SERIAL_ENCODING_ASCII;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,74 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Superclass of all specialised exceptions in this package.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ModbusException extends Exception {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private static final long serialVersionUID = 1L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ModbusException</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusException() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ModbusException</tt> instance with the given
 | 
				
			||||||
 | 
					     * message.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param message the message describing this <tt>ModbusException</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusException(String message) {
 | 
				
			||||||
 | 
					        super(message);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ModbusException</tt> instance with the given
 | 
				
			||||||
 | 
					     * message.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param message the message describing this <tt>ModbusException</tt>.
 | 
				
			||||||
 | 
					     * @param values optional values of the exception
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusException(String message, Object... values) {
 | 
				
			||||||
 | 
					        super(String.format(message, values));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ModbusException</tt> instance with the given
 | 
				
			||||||
 | 
					     * message and underlying cause.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param message the message describing this <tt>ModbusException</tt>.
 | 
				
			||||||
 | 
					     * @param cause   the cause (which is saved for later retrieval by the {@code getCause()} method).
 | 
				
			||||||
 | 
					     *                (A {@code null} value is permitted, and indicates that the cause is nonexistent or unknown.)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusException(String message, Throwable cause) {
 | 
				
			||||||
 | 
					        super(message, cause);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,119 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class that implements a <tt>ModbusIOException</tt>. Instances of this
 | 
				
			||||||
 | 
					 * exception are thrown when errors in the I/O occur.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ModbusIOException extends ModbusException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private static final long serialVersionUID = 1L;
 | 
				
			||||||
 | 
					    private boolean eof = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ModbusIOException</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusIOException() {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ModbusIOException</tt> instance with the given
 | 
				
			||||||
 | 
					     * message.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param message the message describing this <tt>ModbusIOException</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusIOException(String message) {
 | 
				
			||||||
 | 
					        super(message);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ModbusIOException</tt> instance with the given
 | 
				
			||||||
 | 
					     * message.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param message the message describing this <tt>ModbusIOException</tt>.
 | 
				
			||||||
 | 
					     * @param values optional values of the exception
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusIOException(String message, Object... values) {
 | 
				
			||||||
 | 
					        super(message, values);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ModbusIOException</tt> instance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param b true if caused by end of stream, false otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusIOException(boolean b) {
 | 
				
			||||||
 | 
					        eof = b;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ModbusIOException</tt> instance with the given
 | 
				
			||||||
 | 
					     * message.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param message the message describing this <tt>ModbusIOException</tt>.
 | 
				
			||||||
 | 
					     * @param b       true if caused by end of stream, false otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusIOException(String message, boolean b) {
 | 
				
			||||||
 | 
					        super(message);
 | 
				
			||||||
 | 
					        eof = b;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ModbusIOException</tt> instance with the given
 | 
				
			||||||
 | 
					     * message and underlying cause.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param message the message describing this <tt>ModbusIOException</tt>.
 | 
				
			||||||
 | 
					     * @param cause the cause (which is saved for later retrieval by the {@code getCause()} method).
 | 
				
			||||||
 | 
					     * (A {@code null} value is permitted, and indicates that the cause is nonexistent or unknown.)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusIOException(String message, Throwable cause) {
 | 
				
			||||||
 | 
					        super(message, cause);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Tests if this <tt>ModbusIOException</tt> is caused by an end of the
 | 
				
			||||||
 | 
					     * stream.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return true if stream ended, false otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean isEOF() {
 | 
				
			||||||
 | 
					        return eof;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the flag that determines whether this <tt>ModbusIOException</tt> was
 | 
				
			||||||
 | 
					     * caused by an end of the stream.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param b true if stream ended, false otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setEOF(boolean b) {
 | 
				
			||||||
 | 
					        eof = b;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,125 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class that implements a <tt>ModbusSlaveException</tt>. Instances of this
 | 
				
			||||||
 | 
					 * exception are thrown when the slave returns a Modbus exception.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ModbusSlaveException extends ModbusException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private static final long serialVersionUID = 1L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Instance type attribute
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private int type = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ModbusSlaveException</tt> instance with the given
 | 
				
			||||||
 | 
					     * type.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * Types are defined according to the protocol specification in
 | 
				
			||||||
 | 
					     * <tt>net.wimpi.modbus.Modbus</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param type the type of exception that occurred.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusSlaveException(int type) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.type = type;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the exception type message associated with the given exception
 | 
				
			||||||
 | 
					     * number.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param type Numerical value of the Modbus exception.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a String indicating the type of slave exception.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static String getMessage(int type) {
 | 
				
			||||||
 | 
					        switch (type) {
 | 
				
			||||||
 | 
					            case 1:
 | 
				
			||||||
 | 
					                return "Illegal Function";
 | 
				
			||||||
 | 
					            case 2:
 | 
				
			||||||
 | 
					                return "Illegal Data Address";
 | 
				
			||||||
 | 
					            case 3:
 | 
				
			||||||
 | 
					                return "Illegal Data Value";
 | 
				
			||||||
 | 
					            case 4:
 | 
				
			||||||
 | 
					                return "Slave Device Failure";
 | 
				
			||||||
 | 
					            case 5:
 | 
				
			||||||
 | 
					                return "Acknowledge";
 | 
				
			||||||
 | 
					            case 6:
 | 
				
			||||||
 | 
					                return "Slave Device Busy";
 | 
				
			||||||
 | 
					            case 8:
 | 
				
			||||||
 | 
					                return "Memory Parity Error";
 | 
				
			||||||
 | 
					            case 10:
 | 
				
			||||||
 | 
					                return "Gateway Path Unavailable";
 | 
				
			||||||
 | 
					            case 11:
 | 
				
			||||||
 | 
					                return "Gateway Target Device Failed to Respond";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return "Error Code = " + type;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * Returns the type of this <tt>ModbusSlaveException</tt>. <br>
 | 
				
			||||||
 | 
					     * Types are defined according to the protocol specification in
 | 
				
			||||||
 | 
					     * <tt>net.wimpi.modbus.Modbus</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the type of this <tt>ModbusSlaveException</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getType() {
 | 
				
			||||||
 | 
					        return type;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * Tests if this <tt>ModbusSlaveException</tt> is of a given type.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * Types are defined according to the protocol specification in
 | 
				
			||||||
 | 
					     * <tt>net.wimpi.modbus.Modbus</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param TYPE the type to test this <tt>ModbusSlaveException</tt> type
 | 
				
			||||||
 | 
					     *             against.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return true if this <tt>ModbusSlaveException</tt> is of the given type,
 | 
				
			||||||
 | 
					     * false otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean isType(int TYPE) {
 | 
				
			||||||
 | 
					        return (TYPE == type);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the exception type message associated with this exception.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a String indicating the type of slave exception.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public String getMessage() {
 | 
				
			||||||
 | 
					        return getMessage(type);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,495 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.facade;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.ModbusException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.io.AbstractModbusTransport;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.io.ModbusTransaction;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.*;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.InputRegister;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.Register;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.util.BitVector;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Modbus/TCP Master facade - common methods for all the facade implementations
 | 
				
			||||||
 | 
					 * The emphasis is in making callas to Modbus devices as simple as possible
 | 
				
			||||||
 | 
					 * for the most common Function Codes.
 | 
				
			||||||
 | 
					 * This class makes sure that no NPE is raised and that the methods are thread-safe.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					abstract public class AbstractModbusMaster {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final int DEFAULT_UNIT_ID = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected ModbusTransaction transaction;
 | 
				
			||||||
 | 
					    private ReadCoilsRequest readCoilsRequest;
 | 
				
			||||||
 | 
					    private ReadInputDiscretesRequest readInputDiscretesRequest;
 | 
				
			||||||
 | 
					    private WriteCoilRequest writeCoilRequest;
 | 
				
			||||||
 | 
					    private WriteMultipleCoilsRequest writeMultipleCoilsRequest;
 | 
				
			||||||
 | 
					    private ReadInputRegistersRequest readInputRegistersRequest;
 | 
				
			||||||
 | 
					    private ReadMultipleRegistersRequest readMultipleRegistersRequest;
 | 
				
			||||||
 | 
					    private WriteSingleRegisterRequest writeSingleRegisterRequest;
 | 
				
			||||||
 | 
					    private WriteMultipleRegistersRequest writeMultipleRegistersRequest;
 | 
				
			||||||
 | 
					    protected int timeout = Modbus.DEFAULT_TIMEOUT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the transaction to use
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param transaction Transaction to use
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected synchronized void setTransaction(ModbusTransaction transaction) {
 | 
				
			||||||
 | 
					        this.transaction = transaction;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Connects this <tt>ModbusTCPMaster</tt> with the slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws Exception if the connection cannot be established.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    abstract public void connect() throws Exception;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Disconnects this <tt>ModbusTCPMaster</tt> from the slave.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    abstract public void disconnect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reads a given number of coil states from the slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Note that the number of bits in the bit vector will be
 | 
				
			||||||
 | 
					     * forced to the number originally requested.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param unitId the slave unit id.
 | 
				
			||||||
 | 
					     * @param ref    the offset of the coil to start reading from.
 | 
				
			||||||
 | 
					     * @param count  the number of coil states to be read.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a <tt>BitVector</tt> instance holding the
 | 
				
			||||||
 | 
					     * received coil states.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws ModbusException if an I/O error, a slave exception or
 | 
				
			||||||
 | 
					     *                         a transaction error occurs.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized BitVector readCoils(int unitId, int ref, int count) throws ModbusException {
 | 
				
			||||||
 | 
					        checkTransaction();
 | 
				
			||||||
 | 
					        if (readCoilsRequest == null) {
 | 
				
			||||||
 | 
					            readCoilsRequest = new ReadCoilsRequest();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        readCoilsRequest.setUnitID(unitId);
 | 
				
			||||||
 | 
					        readCoilsRequest.setReference(ref);
 | 
				
			||||||
 | 
					        readCoilsRequest.setBitCount(count);
 | 
				
			||||||
 | 
					        transaction.setRequest(readCoilsRequest);
 | 
				
			||||||
 | 
					        transaction.execute();
 | 
				
			||||||
 | 
					        BitVector bv = ((ReadCoilsResponse) getAndCheckResponse()).getCoils();
 | 
				
			||||||
 | 
					        bv.forceSize(count);
 | 
				
			||||||
 | 
					        return bv;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Writes a coil state to the slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param unitId the slave unit id.
 | 
				
			||||||
 | 
					     * @param ref    the offset of the coil to be written.
 | 
				
			||||||
 | 
					     * @param state  the coil state to be written.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the state of the coil as returned from the slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws ModbusException if an I/O error, a slave exception or
 | 
				
			||||||
 | 
					     *                         a transaction error occurs.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized boolean writeCoil(int unitId, int ref, boolean state) throws ModbusException {
 | 
				
			||||||
 | 
					        checkTransaction();
 | 
				
			||||||
 | 
					        if (writeCoilRequest == null) {
 | 
				
			||||||
 | 
					            writeCoilRequest = new WriteCoilRequest();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        writeCoilRequest.setUnitID(unitId);
 | 
				
			||||||
 | 
					        writeCoilRequest.setReference(ref);
 | 
				
			||||||
 | 
					        writeCoilRequest.setCoil(state);
 | 
				
			||||||
 | 
					        transaction.setRequest(writeCoilRequest);
 | 
				
			||||||
 | 
					        transaction.execute();
 | 
				
			||||||
 | 
					        return ((WriteCoilResponse) getAndCheckResponse()).getCoil();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Writes a given number of coil states to the slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Note that the number of coils to be written is given
 | 
				
			||||||
 | 
					     * implicitly, through {@link BitVector#size()}.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param unitId the slave unit id.
 | 
				
			||||||
 | 
					     * @param ref    the offset of the coil to start writing to.
 | 
				
			||||||
 | 
					     * @param coils  a <tt>BitVector</tt> which holds the coil states to be written.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws ModbusException if an I/O error, a slave exception or
 | 
				
			||||||
 | 
					     *                         a transaction error occurs.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized void writeMultipleCoils(int unitId, int ref, BitVector coils) throws ModbusException {
 | 
				
			||||||
 | 
					        checkTransaction();
 | 
				
			||||||
 | 
					        if (writeMultipleCoilsRequest == null) {
 | 
				
			||||||
 | 
					            writeMultipleCoilsRequest = new WriteMultipleCoilsRequest();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        writeMultipleCoilsRequest.setUnitID(unitId);
 | 
				
			||||||
 | 
					        writeMultipleCoilsRequest.setReference(ref);
 | 
				
			||||||
 | 
					        writeMultipleCoilsRequest.setCoils(coils);
 | 
				
			||||||
 | 
					        transaction.setRequest(writeMultipleCoilsRequest);
 | 
				
			||||||
 | 
					        transaction.execute();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reads a given number of input discrete states from the slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Note that the number of bits in the bit vector will be
 | 
				
			||||||
 | 
					     * forced to the number originally requested.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param unitId the slave unit id.
 | 
				
			||||||
 | 
					     * @param ref    the offset of the input discrete to start reading from.
 | 
				
			||||||
 | 
					     * @param count  the number of input discrete states to be read.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a <tt>BitVector</tt> instance holding the received input discrete
 | 
				
			||||||
 | 
					     * states.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws ModbusException if an I/O error, a slave exception or
 | 
				
			||||||
 | 
					     *                         a transaction error occurs.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized BitVector readInputDiscretes(int unitId, int ref, int count) throws ModbusException {
 | 
				
			||||||
 | 
					        checkTransaction();
 | 
				
			||||||
 | 
					        if (readInputDiscretesRequest == null) {
 | 
				
			||||||
 | 
					            readInputDiscretesRequest = new ReadInputDiscretesRequest();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        readInputDiscretesRequest.setUnitID(unitId);
 | 
				
			||||||
 | 
					        readInputDiscretesRequest.setReference(ref);
 | 
				
			||||||
 | 
					        readInputDiscretesRequest.setBitCount(count);
 | 
				
			||||||
 | 
					        transaction.setRequest(readInputDiscretesRequest);
 | 
				
			||||||
 | 
					        transaction.execute();
 | 
				
			||||||
 | 
					        BitVector bv = ((ReadInputDiscretesResponse)getAndCheckResponse()).getDiscretes();
 | 
				
			||||||
 | 
					        bv.forceSize(count);
 | 
				
			||||||
 | 
					        return bv;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reads a given number of input registers from the slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Note that the number of input registers returned (i.e. array length)
 | 
				
			||||||
 | 
					     * will be according to the number received in the slave response.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param unitId the slave unit id.
 | 
				
			||||||
 | 
					     * @param ref    the offset of the input register to start reading from.
 | 
				
			||||||
 | 
					     * @param count  the number of input registers to be read.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a <tt>InputRegister[]</tt> with the received input registers.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws ModbusException if an I/O error, a slave exception or
 | 
				
			||||||
 | 
					     *                         a transaction error occurs.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized InputRegister[] readInputRegisters(int unitId, int ref, int count) throws ModbusException {
 | 
				
			||||||
 | 
					        checkTransaction();
 | 
				
			||||||
 | 
					        if (readInputRegistersRequest == null) {
 | 
				
			||||||
 | 
					            readInputRegistersRequest = new ReadInputRegistersRequest();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        readInputRegistersRequest.setUnitID(unitId);
 | 
				
			||||||
 | 
					        readInputRegistersRequest.setReference(ref);
 | 
				
			||||||
 | 
					        readInputRegistersRequest.setWordCount(count);
 | 
				
			||||||
 | 
					        transaction.setRequest(readInputRegistersRequest);
 | 
				
			||||||
 | 
					        transaction.execute();
 | 
				
			||||||
 | 
					        return ((ReadInputRegistersResponse) getAndCheckResponse()).getRegisters();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reads a given number of registers from the slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Note that the number of registers returned (i.e. array length)
 | 
				
			||||||
 | 
					     * will be according to the number received in the slave response.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param unitId the slave unit id.
 | 
				
			||||||
 | 
					     * @param ref    the offset of the register to start reading from.
 | 
				
			||||||
 | 
					     * @param count  the number of registers to be read.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a <tt>Register[]</tt> holding the received registers.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws ModbusException if an I/O error, a slave exception or
 | 
				
			||||||
 | 
					     *                         a transaction error occurs.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized Register[] readMultipleRegisters(int unitId, int ref, int count) throws ModbusException {
 | 
				
			||||||
 | 
					        checkTransaction();
 | 
				
			||||||
 | 
					        if (readMultipleRegistersRequest == null) {
 | 
				
			||||||
 | 
					            readMultipleRegistersRequest = new ReadMultipleRegistersRequest();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        readMultipleRegistersRequest.setUnitID(unitId);
 | 
				
			||||||
 | 
					        readMultipleRegistersRequest.setReference(ref);
 | 
				
			||||||
 | 
					        readMultipleRegistersRequest.setWordCount(count);
 | 
				
			||||||
 | 
					        transaction.setRequest(readMultipleRegistersRequest);
 | 
				
			||||||
 | 
					        transaction.execute();
 | 
				
			||||||
 | 
					        return ((ReadMultipleRegistersResponse) getAndCheckResponse()).getRegisters();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Writes a single register to the slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param unitId   the slave unit id.
 | 
				
			||||||
 | 
					     * @param ref      the offset of the register to be written.
 | 
				
			||||||
 | 
					     * @param register a <tt>Register</tt> holding the value of the register
 | 
				
			||||||
 | 
					     *                 to be written.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws ModbusException if an I/O error, a slave exception or
 | 
				
			||||||
 | 
					     *                         a transaction error occurs.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized void writeSingleRegister(int unitId, int ref, Register register) throws ModbusException {
 | 
				
			||||||
 | 
					        checkTransaction();
 | 
				
			||||||
 | 
					        if (writeSingleRegisterRequest == null) {
 | 
				
			||||||
 | 
					            writeSingleRegisterRequest = new WriteSingleRegisterRequest();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        writeSingleRegisterRequest.setUnitID(unitId);
 | 
				
			||||||
 | 
					        writeSingleRegisterRequest.setReference(ref);
 | 
				
			||||||
 | 
					        writeSingleRegisterRequest.setRegister(register);
 | 
				
			||||||
 | 
					        transaction.setRequest(writeSingleRegisterRequest);
 | 
				
			||||||
 | 
					        transaction.execute();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Writes a number of registers to the slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param unitId    the slave unit id.
 | 
				
			||||||
 | 
					     * @param ref       the offset of the register to start writing to.
 | 
				
			||||||
 | 
					     * @param registers a <tt>Register[]</tt> holding the values of
 | 
				
			||||||
 | 
					     *                  the registers to be written.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws ModbusException if an I/O error, a slave exception or
 | 
				
			||||||
 | 
					     *                         a transaction error occurs.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized void writeMultipleRegisters(int unitId, int ref, Register[] registers) throws ModbusException {
 | 
				
			||||||
 | 
					        checkTransaction();
 | 
				
			||||||
 | 
					        if (writeMultipleRegistersRequest == null) {
 | 
				
			||||||
 | 
					            writeMultipleRegistersRequest = new WriteMultipleRegistersRequest();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        writeMultipleRegistersRequest.setUnitID(unitId);
 | 
				
			||||||
 | 
					        writeMultipleRegistersRequest.setReference(ref);
 | 
				
			||||||
 | 
					        writeMultipleRegistersRequest.setRegisters(registers);
 | 
				
			||||||
 | 
					        transaction.setRequest(writeMultipleRegistersRequest);
 | 
				
			||||||
 | 
					        transaction.execute();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reads a given number of coil states from the slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Note that the number of bits in the bit vector will be
 | 
				
			||||||
 | 
					     * forced to the number originally requested.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref   the offset of the coil to start reading from.
 | 
				
			||||||
 | 
					     * @param count the number of coil states to be read.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a <tt>BitVector</tt> instance holding the
 | 
				
			||||||
 | 
					     * received coil states.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws ModbusException if an I/O error, a slave exception or
 | 
				
			||||||
 | 
					     *                         a transaction error occurs.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized BitVector readCoils(int ref, int count) throws ModbusException {
 | 
				
			||||||
 | 
					        return readCoils(DEFAULT_UNIT_ID, ref, count);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Writes a coil state to the slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref   the offset of the coil to be written.
 | 
				
			||||||
 | 
					     * @param state the coil state to be written.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the state of the coil as returned from the slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws ModbusException if an I/O error, a slave exception or
 | 
				
			||||||
 | 
					     *                         a transaction error occurs.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized boolean writeCoil(int ref, boolean state) throws ModbusException {
 | 
				
			||||||
 | 
					        return writeCoil(DEFAULT_UNIT_ID, ref, state);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Writes a given number of coil states to the slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Note that the number of coils to be written is given
 | 
				
			||||||
 | 
					     * implicitly, through {@link BitVector#size()}.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref   the offset of the coil to start writing to.
 | 
				
			||||||
 | 
					     * @param coils a <tt>BitVector</tt> which holds the coil states to be written.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws ModbusException if an I/O error, a slave exception or
 | 
				
			||||||
 | 
					     *                         a transaction error occurs.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized void writeMultipleCoils(int ref, BitVector coils) throws ModbusException {
 | 
				
			||||||
 | 
					        writeMultipleCoils(DEFAULT_UNIT_ID, ref, coils);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reads a given number of input discrete states from the slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Note that the number of bits in the bit vector will be
 | 
				
			||||||
 | 
					     * forced to the number originally requested.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref   the offset of the input discrete to start reading from.
 | 
				
			||||||
 | 
					     * @param count the number of input discrete states to be read.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a <tt>BitVector</tt> instance holding the received input discrete
 | 
				
			||||||
 | 
					     * states.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws ModbusException if an I/O error, a slave exception or
 | 
				
			||||||
 | 
					     *                         a transaction error occurs.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized BitVector readInputDiscretes(int ref, int count) throws ModbusException {
 | 
				
			||||||
 | 
					        return readInputDiscretes(DEFAULT_UNIT_ID, ref, count);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reads a given number of input registers from the slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Note that the number of input registers returned (i.e. array length)
 | 
				
			||||||
 | 
					     * will be according to the number received in the slave response.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref   the offset of the input register to start reading from.
 | 
				
			||||||
 | 
					     * @param count the number of input registers to be read.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a <tt>InputRegister[]</tt> with the received input registers.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws ModbusException if an I/O error, a slave exception or
 | 
				
			||||||
 | 
					     *                         a transaction error occurs.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized InputRegister[] readInputRegisters(int ref, int count) throws ModbusException {
 | 
				
			||||||
 | 
					        return readInputRegisters(DEFAULT_UNIT_ID, ref, count);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reads a given number of registers from the slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Note that the number of registers returned (i.e. array length)
 | 
				
			||||||
 | 
					     * will be according to the number received in the slave response.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref   the offset of the register to start reading from.
 | 
				
			||||||
 | 
					     * @param count the number of registers to be read.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a <tt>Register[]</tt> holding the received registers.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws ModbusException if an I/O error, a slave exception or
 | 
				
			||||||
 | 
					     *                         a transaction error occurs.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized Register[] readMultipleRegisters(int ref, int count) throws ModbusException {
 | 
				
			||||||
 | 
					        return readMultipleRegisters(DEFAULT_UNIT_ID, ref, count);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Writes a single register to the slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref      the offset of the register to be written.
 | 
				
			||||||
 | 
					     * @param register a <tt>Register</tt> holding the value of the register
 | 
				
			||||||
 | 
					     *                 to be written.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws ModbusException if an I/O error, a slave exception or
 | 
				
			||||||
 | 
					     *                         a transaction error occurs.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized void writeSingleRegister(int ref, Register register) throws ModbusException {
 | 
				
			||||||
 | 
					        writeSingleRegister(DEFAULT_UNIT_ID, ref, register);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Writes a number of registers to the slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref       the offset of the register to start writing to.
 | 
				
			||||||
 | 
					     * @param registers a <tt>Register[]</tt> holding the values of
 | 
				
			||||||
 | 
					     *                  the registers to be written.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws ModbusException if an I/O error, a slave exception or
 | 
				
			||||||
 | 
					     *                         a transaction error occurs.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized void writeMultipleRegisters(int ref, Register[] registers) throws ModbusException {
 | 
				
			||||||
 | 
					        writeMultipleRegisters(DEFAULT_UNIT_ID, ref, registers);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reads the response from the transaction
 | 
				
			||||||
 | 
					     * If there is no response, then it throws an error
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Modbus response
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws ModbusException If response is null
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private ModbusResponse getAndCheckResponse() throws ModbusException {
 | 
				
			||||||
 | 
					        ModbusResponse res = transaction.getResponse();
 | 
				
			||||||
 | 
					        if (res == null) {
 | 
				
			||||||
 | 
					            throw new ModbusException("No response");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return res;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Checks to make sure there is a transaction to use
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws ModbusException If transaction is null
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void checkTransaction() throws ModbusException {
 | 
				
			||||||
 | 
					        if (transaction == null) {
 | 
				
			||||||
 | 
					            throw new ModbusException("No transaction created, probably not connected");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the receive timeout in milliseconds
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Timeout in milliseconds
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getTimeout() {
 | 
				
			||||||
 | 
					        return timeout;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the receive timeout
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param timeout Timeout in milliseconds
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setTimeout(int timeout) {
 | 
				
			||||||
 | 
					        this.timeout = timeout;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Set the amount of retries for opening
 | 
				
			||||||
 | 
					     * the connection for executing the transaction.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param retries the amount of retries as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    synchronized public void setRetries(int retries) {
 | 
				
			||||||
 | 
					        if (transaction != null) {
 | 
				
			||||||
 | 
					            transaction.setRetries(retries);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the flag that controls whether the
 | 
				
			||||||
 | 
					     * validity of a transaction will be checked.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param b true if checking validity, false otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    synchronized public void setCheckingValidity(boolean b) {
 | 
				
			||||||
 | 
					        if (transaction != null) {
 | 
				
			||||||
 | 
					            transaction.setCheckingValidity(b);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the transport being used by the
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return ModbusTransport
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract AbstractModbusTransport getTransport();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,190 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.facade;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.io.AbstractModbusTransport;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.io.ModbusTCPTransaction;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.TCPMasterConnection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.net.InetAddress;
 | 
				
			||||||
 | 
					import java.net.UnknownHostException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Modbus/TCP Master facade.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ModbusTCPMaster extends AbstractModbusMaster {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private TCPMasterConnection connection;
 | 
				
			||||||
 | 
					    private boolean reconnecting = false;
 | 
				
			||||||
 | 
					    private boolean useRtuOverTcp = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new master facade instance for communication
 | 
				
			||||||
 | 
					     * with a given slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param addr an internet address as resolvable IP name or IP number,
 | 
				
			||||||
 | 
					     *             specifying the slave to communicate with.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusTCPMaster(String addr) {
 | 
				
			||||||
 | 
					        this(addr, Modbus.DEFAULT_PORT, Modbus.DEFAULT_TIMEOUT, false, false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new master facade instance for communication
 | 
				
			||||||
 | 
					     * with a given slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param addr an internet address as resolvable IP name or IP number,
 | 
				
			||||||
 | 
					     *             specifying the slave to communicate with.
 | 
				
			||||||
 | 
					     * @param useRtuOverTcp True if the RTU protocol should be used over TCP
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusTCPMaster(String addr, boolean useRtuOverTcp) {
 | 
				
			||||||
 | 
					        this(addr, Modbus.DEFAULT_PORT, Modbus.DEFAULT_TIMEOUT, false, useRtuOverTcp);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new master facade instance for communication
 | 
				
			||||||
 | 
					     * with a given slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param addr an internet address as resolvable IP name or IP number,
 | 
				
			||||||
 | 
					     *             specifying the slave to communicate with.
 | 
				
			||||||
 | 
					     * @param port the port the slave is listening to.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusTCPMaster(String addr, int port) {
 | 
				
			||||||
 | 
					        this(addr, port, Modbus.DEFAULT_TIMEOUT, false, false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new master facade instance for communication
 | 
				
			||||||
 | 
					     * with a given slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param addr an internet address as resolvable IP name or IP number,
 | 
				
			||||||
 | 
					     *             specifying the slave to communicate with.
 | 
				
			||||||
 | 
					     * @param port the port the slave is listening to.
 | 
				
			||||||
 | 
					     * @param useRtuOverTcp True if the RTU protocol should be used over TCP
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusTCPMaster(String addr, int port, boolean useRtuOverTcp) {
 | 
				
			||||||
 | 
					        this(addr, port, Modbus.DEFAULT_TIMEOUT, false, useRtuOverTcp);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new master facade instance for communication
 | 
				
			||||||
 | 
					     * with a given slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param addr      an internet address as resolvable IP name or IP number,
 | 
				
			||||||
 | 
					     *                  specifying the slave to communicate with.
 | 
				
			||||||
 | 
					     * @param port      the port the slave is listening to.
 | 
				
			||||||
 | 
					     * @param timeout   Socket timeout in milliseconds
 | 
				
			||||||
 | 
					     * @param reconnect True if the socket should reconnect if it detects a connection failure
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusTCPMaster(String addr, int port, int timeout, boolean reconnect) {
 | 
				
			||||||
 | 
					        this(addr, port, timeout, reconnect, false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new master facade instance for communication
 | 
				
			||||||
 | 
					     * with a given slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param addr      an internet address as resolvable IP name or IP number,
 | 
				
			||||||
 | 
					     *                  specifying the slave to communicate with.
 | 
				
			||||||
 | 
					     * @param port      the port the slave is listening to.
 | 
				
			||||||
 | 
					     * @param timeout   Socket timeout in milliseconds
 | 
				
			||||||
 | 
					     * @param reconnect True if the socket should reconnect if it detcts a connection failure
 | 
				
			||||||
 | 
					     * @param useRtuOverTcp True if the RTU protocol should be used over TCP
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusTCPMaster(String addr, int port, int timeout, boolean reconnect, boolean useRtuOverTcp) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					        this.useRtuOverTcp = useRtuOverTcp;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            InetAddress slaveAddress = InetAddress.getByName(addr);
 | 
				
			||||||
 | 
					            connection = new TCPMasterConnection(slaveAddress);
 | 
				
			||||||
 | 
					            connection.setPort(port);
 | 
				
			||||||
 | 
					            connection.setTimeout(timeout);
 | 
				
			||||||
 | 
					            this.timeout = timeout;
 | 
				
			||||||
 | 
					            setReconnecting(reconnect);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (UnknownHostException e) {
 | 
				
			||||||
 | 
					            throw new RuntimeException("Failed to contruct ModbusTCPMaster instance.", e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Connects this <tt>ModbusTCPMaster</tt> with the slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws Exception if the connection cannot be established.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized void connect() throws Exception {
 | 
				
			||||||
 | 
					        if (connection != null && !connection.isConnected()) {
 | 
				
			||||||
 | 
					            connection.connect(useRtuOverTcp);
 | 
				
			||||||
 | 
					            transaction = connection.getModbusTransport().createTransaction();
 | 
				
			||||||
 | 
					            ((ModbusTCPTransaction)transaction).setReconnecting(reconnecting);
 | 
				
			||||||
 | 
					            setTransaction(transaction);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Disconnects this <tt>ModbusTCPMaster</tt> from the slave.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized void disconnect() {
 | 
				
			||||||
 | 
					        if (connection != null && connection.isConnected()) {
 | 
				
			||||||
 | 
					            connection.close();
 | 
				
			||||||
 | 
					            transaction = null;
 | 
				
			||||||
 | 
					            setTransaction(null);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Tests if a constant connection is maintained or if a new
 | 
				
			||||||
 | 
					     * connection is established for every transaction.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return true if a new connection should be established for each
 | 
				
			||||||
 | 
					     * transaction, false otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean isReconnecting() {
 | 
				
			||||||
 | 
					        return reconnecting;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the flag that specifies whether to maintain a
 | 
				
			||||||
 | 
					     * constant connection or reconnect for every transaction.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param b true if a new connection should be established for each
 | 
				
			||||||
 | 
					     *          transaction, false otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized void setReconnecting(boolean b) {
 | 
				
			||||||
 | 
					        reconnecting = b;
 | 
				
			||||||
 | 
					        if (transaction != null) {
 | 
				
			||||||
 | 
					            ((ModbusTCPTransaction)transaction).setReconnecting(b);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void setTimeout(int timeout) {
 | 
				
			||||||
 | 
					        super.setTimeout(timeout);
 | 
				
			||||||
 | 
					        if (connection != null) {
 | 
				
			||||||
 | 
					            connection.setTimeout(timeout);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public AbstractModbusTransport getTransport() {
 | 
				
			||||||
 | 
					        return connection == null ? null : connection.getModbusTransport();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,121 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.facade;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.io.AbstractModbusTransport;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.UDPMasterConnection;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.net.InetAddress;
 | 
				
			||||||
 | 
					import java.net.UnknownHostException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Modbus/UDP Master facade.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ModbusUDPMaster extends AbstractModbusMaster {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final Logger logger = LoggerFactory.getLogger(ModbusUDPMaster.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private UDPMasterConnection connection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new master facade instance for communication
 | 
				
			||||||
 | 
					     * with a given slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param addr an internet address as resolvable IP name or IP number,
 | 
				
			||||||
 | 
					     *             specifying the slave to communicate with.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusUDPMaster(String addr) {
 | 
				
			||||||
 | 
					        this(addr, Modbus.DEFAULT_PORT);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new master facade instance for communication
 | 
				
			||||||
 | 
					     * with a given slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param addr an internet address as resolvable IP name or IP number,
 | 
				
			||||||
 | 
					     *             specifying the slave to communicate with.
 | 
				
			||||||
 | 
					     * @param port the port the slave is listening to.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusUDPMaster(String addr, int port) {
 | 
				
			||||||
 | 
					        this(addr, port, Modbus.DEFAULT_TIMEOUT);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new master facade instance for communication
 | 
				
			||||||
 | 
					     * with a given slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param addr    an internet address as resolvable IP name or IP number,
 | 
				
			||||||
 | 
					     *                specifying the slave to communicate with.
 | 
				
			||||||
 | 
					     * @param port    the port the slave is listening to.
 | 
				
			||||||
 | 
					     * @param timeout Socket timeout in milliseconds
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusUDPMaster(String addr, int port, int timeout) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            InetAddress slaveAddress = InetAddress.getByName(addr);
 | 
				
			||||||
 | 
					            connection = new UDPMasterConnection(slaveAddress);
 | 
				
			||||||
 | 
					            connection.setPort(port);
 | 
				
			||||||
 | 
					            connection.setTimeout(timeout);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (UnknownHostException e) {
 | 
				
			||||||
 | 
					            throw new RuntimeException("Failed to construct ModbusUDPMaster instance.", e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Connects this <tt>ModbusTCPMaster</tt> with the slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws Exception if the connection cannot be established.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized void connect() throws Exception {
 | 
				
			||||||
 | 
					        if (connection != null && !connection.isConnected()) {
 | 
				
			||||||
 | 
					            connection.connect();
 | 
				
			||||||
 | 
					            transaction = connection.getModbusTransport().createTransaction();
 | 
				
			||||||
 | 
					            setTransaction(transaction);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Disconnects this <tt>ModbusTCPMaster</tt> from the slave.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized void disconnect() {
 | 
				
			||||||
 | 
					        if (connection != null && connection.isConnected()) {
 | 
				
			||||||
 | 
					            connection.close();
 | 
				
			||||||
 | 
					            transaction = null;
 | 
				
			||||||
 | 
					            setTransaction(null);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void setTimeout(int timeout) {
 | 
				
			||||||
 | 
					        super.setTimeout(timeout);
 | 
				
			||||||
 | 
					        if (connection != null) {
 | 
				
			||||||
 | 
					            connection.setTimeout(timeout);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public AbstractModbusTransport getTransport() {
 | 
				
			||||||
 | 
					        return connection == null ? null : connection.getModbusTransport();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,118 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.io;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.ModbusIOException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ModbusRequest;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ModbusResponse;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractModbusListener;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Interface defining the I/O mechanisms for
 | 
				
			||||||
 | 
					 * <tt>ModbusMessage</tt> instances.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public abstract class AbstractModbusTransport {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected int timeout = Modbus.DEFAULT_TIMEOUT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Set the socket timeout
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param time Timeout in milliseconds
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setTimeout(int time) {
 | 
				
			||||||
 | 
					        timeout = time;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Closes the raw input and output streams of
 | 
				
			||||||
 | 
					     * this <tt>ModbusTransport</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IOException if a stream
 | 
				
			||||||
 | 
					     *                     cannot be closed properly.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract void close() throws IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates a Modbus transaction for the underlying transport.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the new transaction
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract ModbusTransaction createTransaction();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Writes a <tt>ModbusMessage</tt> to the
 | 
				
			||||||
 | 
					     * output stream of this <tt>ModbusTransport</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param msg a <tt>ModbusMessage</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws ModbusIOException data cannot be
 | 
				
			||||||
 | 
					     *                           written properly to the raw output stream of
 | 
				
			||||||
 | 
					     *                           this <tt>ModbusTransport</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract void writeRequest(ModbusRequest msg) throws ModbusIOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Writes a <tt>ModbusResponseMessage</tt> to the
 | 
				
			||||||
 | 
					     * output stream of this <tt>ModbusTransport</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param msg a <tt>ModbusMessage</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws ModbusIOException data cannot be
 | 
				
			||||||
 | 
					     *                           written properly to the raw output stream of
 | 
				
			||||||
 | 
					     *                           this <tt>ModbusTransport</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract void writeResponse(ModbusResponse msg) throws ModbusIOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reads a <tt>ModbusRequest</tt> from the
 | 
				
			||||||
 | 
					     * input stream of this <tt>ModbusTransport</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param listener Listener the request was received by
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return req the <tt>ModbusRequest</tt> read from the underlying stream.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws ModbusIOException data cannot be
 | 
				
			||||||
 | 
					     *                           read properly from the raw input stream of
 | 
				
			||||||
 | 
					     *                           this <tt>ModbusTransport</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract ModbusRequest readRequest(AbstractModbusListener listener) throws ModbusIOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reads a <tt>ModbusResponse</tt> from the
 | 
				
			||||||
 | 
					     * input stream of this <tt>ModbusTransport</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return res the <tt>ModbusResponse</tt> read from the underlying stream.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws ModbusIOException data cannot be
 | 
				
			||||||
 | 
					     *                           read properly from the raw input stream of
 | 
				
			||||||
 | 
					     *                           this <tt>ModbusTransport</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract ModbusResponse readResponse() throws ModbusIOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,83 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.io;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ModbusMessage;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ModbusRequest;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ModbusResponse;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractSerialConnection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Any class that wants to listen for the begining and ending of read/writes
 | 
				
			||||||
 | 
					 * to the Serial channel need to implement this interface
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					abstract public class AbstractSerialTransportListener {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Will be called whenever a message is about to be written
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param port Port being used
 | 
				
			||||||
 | 
					     * @param msg  Message to be written
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void beforeMessageWrite(AbstractSerialConnection port, ModbusMessage msg) {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Will be called whenever a message has been written
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param port Port being used
 | 
				
			||||||
 | 
					     * @param msg  Message written
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void afterMessageWrite(AbstractSerialConnection port, ModbusMessage msg) {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Called before a request is read
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param port Port to read
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void beforeRequestRead(AbstractSerialConnection port) {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Called whenever a request has been received
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param port Port to read
 | 
				
			||||||
 | 
					     * @param req  Request received
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void afterRequestRead(AbstractSerialConnection port, ModbusRequest req) {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Called before a response is read
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param port Port to read
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void beforeResponseRead(AbstractSerialConnection port) {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Called whenever a response has been received
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param port Port to read
 | 
				
			||||||
 | 
					     * @param res  Response received
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void afterResponseRead(AbstractSerialConnection port, ModbusResponse res) {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,184 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.io;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataInputStream;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a byte array input stream with
 | 
				
			||||||
 | 
					 * a DataInput interface.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class BytesInputStream
 | 
				
			||||||
 | 
					        extends FastByteArrayInputStream implements DataInput {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    DataInputStream dataInputStream;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>BytesInputStream</tt> instance,
 | 
				
			||||||
 | 
					     * with an empty buffer of a given size.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param size the size of the input buffer.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public BytesInputStream(int size) {
 | 
				
			||||||
 | 
					        super(new byte[size]);
 | 
				
			||||||
 | 
					        dataInputStream = new DataInputStream(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>BytesInputStream</tt> instance,
 | 
				
			||||||
 | 
					     * that will read from the given data.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param data a byte array containing data to be read.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public BytesInputStream(byte[] data) {
 | 
				
			||||||
 | 
					        super(data);
 | 
				
			||||||
 | 
					        dataInputStream = new DataInputStream(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Resets this <tt>BytesInputStream</tt> using the given
 | 
				
			||||||
 | 
					     * byte[] as new input buffer.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param data a byte array with data to be read.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void reset(byte[] data) {
 | 
				
			||||||
 | 
					        pos = 0;
 | 
				
			||||||
 | 
					        mark = 0;
 | 
				
			||||||
 | 
					        buf = data;
 | 
				
			||||||
 | 
					        count = data.length;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Resets this <tt>BytesInputStream</tt> using the given
 | 
				
			||||||
 | 
					     * byte[] as new input buffer and a given length.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param data   a byte array with data to be read.
 | 
				
			||||||
 | 
					     * @param length the length of the buffer to be considered.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void reset(byte[] data, int length) {
 | 
				
			||||||
 | 
					        pos = 0;
 | 
				
			||||||
 | 
					        mark = 0;
 | 
				
			||||||
 | 
					        count = length;
 | 
				
			||||||
 | 
					        buf = data;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Resets this <tt>BytesInputStream</tt>  assigning the input buffer
 | 
				
			||||||
 | 
					     * a new length.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param length the length of the buffer to be considered.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void reset(int length) {
 | 
				
			||||||
 | 
					        pos = 0;
 | 
				
			||||||
 | 
					        count = length;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Skips the given number of bytes or all bytes till the end
 | 
				
			||||||
 | 
					     * of the assigned input buffer length.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param n the number of bytes to be skipped as <tt>int</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of bytes skipped.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int skip(int n) {
 | 
				
			||||||
 | 
					        mark(pos);
 | 
				
			||||||
 | 
					        pos += n;
 | 
				
			||||||
 | 
					        return n;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the reference to the input buffer.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the reference to the <tt>byte[]</tt> input buffer.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized byte[] getBuffer() {
 | 
				
			||||||
 | 
					        byte[] dest = new byte[buf.length];
 | 
				
			||||||
 | 
					        System.arraycopy(buf, 0, dest, 0, dest.length);
 | 
				
			||||||
 | 
					        return dest;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int getBufferLength() {
 | 
				
			||||||
 | 
					        return buf.length;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readFully(byte b[]) throws IOException {
 | 
				
			||||||
 | 
					        dataInputStream.readFully(b);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readFully(byte b[], int off, int len) throws IOException {
 | 
				
			||||||
 | 
					        dataInputStream.readFully(b, off, len);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int skipBytes(int n) throws IOException {
 | 
				
			||||||
 | 
					        return dataInputStream.skipBytes(n);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean readBoolean() throws IOException {
 | 
				
			||||||
 | 
					        return dataInputStream.readBoolean();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte readByte() throws IOException {
 | 
				
			||||||
 | 
					        return dataInputStream.readByte();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int readUnsignedByte() throws IOException {
 | 
				
			||||||
 | 
					        return dataInputStream.readUnsignedByte();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public short readShort() throws IOException {
 | 
				
			||||||
 | 
					        return dataInputStream.readShort();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int readUnsignedShort() throws IOException {
 | 
				
			||||||
 | 
					        return dataInputStream.readUnsignedShort();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public char readChar() throws IOException {
 | 
				
			||||||
 | 
					        return dataInputStream.readChar();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int readInt() throws IOException {
 | 
				
			||||||
 | 
					        return dataInputStream.readInt();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public long readLong() throws IOException {
 | 
				
			||||||
 | 
					        return dataInputStream.readLong();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public float readFloat() throws IOException {
 | 
				
			||||||
 | 
					        return dataInputStream.readFloat();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public double readDouble() throws IOException {
 | 
				
			||||||
 | 
					        return dataInputStream.readDouble();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String readLine() throws IOException {
 | 
				
			||||||
 | 
					        throw new IOException("Not supported");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String readUTF() throws IOException {
 | 
				
			||||||
 | 
					        return dataInputStream.readUTF();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,119 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.io;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.DataOutputStream;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a byte array output stream with
 | 
				
			||||||
 | 
					 * a DataInput interface.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class BytesOutputStream extends FastByteArrayOutputStream implements DataOutput {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private DataOutputStream dataOutputStream;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>BytesOutputStream</tt> instance with
 | 
				
			||||||
 | 
					     * a new output buffer of the given size.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param size the size of the output buffer as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public BytesOutputStream(int size) {
 | 
				
			||||||
 | 
					        super(size);
 | 
				
			||||||
 | 
					        dataOutputStream = new DataOutputStream(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>BytesOutputStream</tt> instance with
 | 
				
			||||||
 | 
					     * a given output buffer.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param buffer the output buffer as <tt>byte[]</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public BytesOutputStream(byte[] buffer) {
 | 
				
			||||||
 | 
					        buf = buffer;
 | 
				
			||||||
 | 
					        count = 0;
 | 
				
			||||||
 | 
					        dataOutputStream = new DataOutputStream(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the reference to the output buffer.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the reference to the <tt>byte[]</tt> output buffer.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized byte[] getBuffer() {
 | 
				
			||||||
 | 
					        byte[] dest = new byte[buf.length];
 | 
				
			||||||
 | 
					        System.arraycopy(buf, 0, dest, 0, dest.length);
 | 
				
			||||||
 | 
					        return dest;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void reset() {
 | 
				
			||||||
 | 
					        count = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeBoolean(boolean v) throws IOException {
 | 
				
			||||||
 | 
					        dataOutputStream.writeBoolean(v);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeByte(int v) throws IOException {
 | 
				
			||||||
 | 
					        dataOutputStream.writeByte(v);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeShort(int v) throws IOException {
 | 
				
			||||||
 | 
					        dataOutputStream.writeShort(v);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeChar(int v) throws IOException {
 | 
				
			||||||
 | 
					        dataOutputStream.writeChar(v);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeInt(int v) throws IOException {
 | 
				
			||||||
 | 
					        dataOutputStream.writeInt(v);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeLong(long v) throws IOException {
 | 
				
			||||||
 | 
					        dataOutputStream.writeLong(v);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeFloat(float v) throws IOException {
 | 
				
			||||||
 | 
					        dataOutputStream.writeFloat(v);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeDouble(double v) throws IOException {
 | 
				
			||||||
 | 
					        dataOutputStream.writeDouble(v);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeBytes(String s) throws IOException {
 | 
				
			||||||
 | 
					        int len = s.length();
 | 
				
			||||||
 | 
					        for (int i = 0; i < len; i++) {
 | 
				
			||||||
 | 
					            this.write((byte)s.charAt(i));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeChars(String s) throws IOException {
 | 
				
			||||||
 | 
					        dataOutputStream.writeChars(s);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeUTF(String str) throws IOException {
 | 
				
			||||||
 | 
					        dataOutputStream.writeUTF(str);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,166 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.io;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.io.InputStream;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * This class is a replacement for ByteArrayInputStream that does not
 | 
				
			||||||
 | 
					 * synchronize every byte read.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Mark Hayes
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class FastByteArrayInputStream extends InputStream {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final Logger logger = LoggerFactory.getLogger(FastByteArrayInputStream.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Number of bytes in the input buffer.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected int count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Actual position pointer into the input buffer.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int pos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Marked position pointer into the input buffer.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int mark;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Input buffer <tt>byte[]</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    byte[] buf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates an input stream.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param buffer the data to read.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    FastByteArrayInputStream(byte[] buffer) {
 | 
				
			||||||
 | 
					        buf = buffer;
 | 
				
			||||||
 | 
					        count = buffer.length;
 | 
				
			||||||
 | 
					        pos = 0;
 | 
				
			||||||
 | 
					        mark = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // --- begin ByteArrayInputStream compatible methods ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int read() throws IOException {
 | 
				
			||||||
 | 
					        logger.debug("read()");
 | 
				
			||||||
 | 
					        logger.debug("count={} pos={}", count, pos);
 | 
				
			||||||
 | 
					        return (pos < count) ? (buf[pos++] & 0xff) : (-1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int read(byte[] toBuf) throws IOException {
 | 
				
			||||||
 | 
					        logger.debug("read(byte[])");
 | 
				
			||||||
 | 
					        return read(toBuf, 0, toBuf.length);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int read(byte[] toBuf, int offset, int length) throws IOException {
 | 
				
			||||||
 | 
					        logger.debug("read(byte[],int,int)");
 | 
				
			||||||
 | 
					        int avail = count - pos;
 | 
				
			||||||
 | 
					        if (avail <= 0) {
 | 
				
			||||||
 | 
					            return -1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (length > avail) {
 | 
				
			||||||
 | 
					            length = avail;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        for (int i = 0; i < length; i++) {
 | 
				
			||||||
 | 
					            toBuf[offset++] = buf[pos++];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return length;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public long skip(long count) {
 | 
				
			||||||
 | 
					        int myCount = (int)count;
 | 
				
			||||||
 | 
					        if (myCount + pos > this.count) {
 | 
				
			||||||
 | 
					            myCount = this.count - pos;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        pos += myCount;
 | 
				
			||||||
 | 
					        return myCount;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int available() {
 | 
				
			||||||
 | 
					        return count - pos;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int getCount() {
 | 
				
			||||||
 | 
					        return count;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void mark(int readlimit) {
 | 
				
			||||||
 | 
					        logger.debug("mark()");
 | 
				
			||||||
 | 
					        mark = pos;
 | 
				
			||||||
 | 
					        logger.debug("mark={} pos={}", mark, pos);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void reset() {
 | 
				
			||||||
 | 
					        logger.debug("reset()");
 | 
				
			||||||
 | 
					        pos = mark;
 | 
				
			||||||
 | 
					        logger.debug("mark={} pos={}", mark, pos);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean markSupported() {
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // --- end ByteArrayInputStream compatible methods ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] toByteArray() {
 | 
				
			||||||
 | 
					        byte[] toBuf = new byte[count];
 | 
				
			||||||
 | 
					        System.arraycopy(buf, 0, toBuf, 0, count);
 | 
				
			||||||
 | 
					        return toBuf;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the underlying data being read.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the underlying data.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized byte[] getBufferBytes() {
 | 
				
			||||||
 | 
					        byte[] dest = new byte[count];
 | 
				
			||||||
 | 
					        System.arraycopy(buf, 0, dest, 0, dest.length);
 | 
				
			||||||
 | 
					        return dest;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the offset at which data is being read from the buffer.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the offset at which data is being read.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getBufferOffset() {
 | 
				
			||||||
 | 
					        return pos;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the end of the buffer being read.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the end of the buffer.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getBufferLength() {
 | 
				
			||||||
 | 
					        return count;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,268 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.io;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.io.OutputStream;
 | 
				
			||||||
 | 
					import java.io.UnsupportedEncodingException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * This class is a replacement implementation for ByteArrayOutputStream
 | 
				
			||||||
 | 
					 * that does not synchronize every
 | 
				
			||||||
 | 
					 * byte written.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Mark Hayes
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class FastByteArrayOutputStream extends OutputStream {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final Logger logger = LoggerFactory.getLogger(FastByteArrayOutputStream.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the default oputput buffer size (100 bytes).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private static final int DEFAULT_INIT_SIZE = 100;
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Defines the default increment of the output buffer size
 | 
				
			||||||
 | 
					     * (100 bytes).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private static final int DEFAULT_BUMP_SIZE = 100;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Number of bytes in the output buffer.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected int count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Increment of the output buffer size on overflow.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private int bumpLen;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Output buffer <tt>byte[]</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    byte[] buf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates an output stream with default sizes.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    FastByteArrayOutputStream() {
 | 
				
			||||||
 | 
					        buf = new byte[DEFAULT_INIT_SIZE];
 | 
				
			||||||
 | 
					        bumpLen = DEFAULT_BUMP_SIZE;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates an output stream with a default bump size and a given initial
 | 
				
			||||||
 | 
					     * size.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param initialSize the initial size of the buffer.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    FastByteArrayOutputStream(int initialSize) {
 | 
				
			||||||
 | 
					        buf = new byte[initialSize];
 | 
				
			||||||
 | 
					        bumpLen = DEFAULT_BUMP_SIZE;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates an output stream with a given bump size and initial size.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param initialSize the initial size of the buffer.
 | 
				
			||||||
 | 
					     * @param bumpSize    the amount to increment the buffer.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public FastByteArrayOutputStream(int initialSize, int bumpSize) {
 | 
				
			||||||
 | 
					        buf = new byte[initialSize];
 | 
				
			||||||
 | 
					        bumpLen = bumpSize;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // --- begin ByteArrayOutputStream compatible methods ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the number of bytes written to this
 | 
				
			||||||
 | 
					     * <tt>FastByteArrayOutputStream</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of bytes written as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int size() {
 | 
				
			||||||
 | 
					        return count;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Resets this <tt>FastByteArrayOutputStream</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void reset() {
 | 
				
			||||||
 | 
					        count = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void write(int b) throws IOException {
 | 
				
			||||||
 | 
					        if (count + 1 > buf.length) {
 | 
				
			||||||
 | 
					            bump(1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        buf[count++] = (byte)b;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void write(byte[] fromBuf) throws IOException {
 | 
				
			||||||
 | 
					        int needed = count + fromBuf.length - buf.length;
 | 
				
			||||||
 | 
					        if (needed > 0) {
 | 
				
			||||||
 | 
					            bump(needed);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        for (byte aFromBuf : fromBuf) {
 | 
				
			||||||
 | 
					            buf[count++] = aFromBuf;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void write(byte[] fromBuf, int offset, int length) throws IOException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int needed = count + length - buf.length;
 | 
				
			||||||
 | 
					        if (needed > 0) {
 | 
				
			||||||
 | 
					            bump(needed);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        int fromLen = offset + length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (int i = offset; i < fromLen; i++) {
 | 
				
			||||||
 | 
					            buf[count++] = fromBuf[i];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Writes the content of this <tt>FastByteArrayOutputStream</tt>
 | 
				
			||||||
 | 
					     * to the given output stream.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param out the output stream to be written to.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IOException if an I/O error occurs.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized void writeTo(OutputStream out) throws IOException {
 | 
				
			||||||
 | 
					        out.write(buf, 0, count);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public String toString() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            return new String(buf, 0, count, "US-ASCII");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (Exception e) {
 | 
				
			||||||
 | 
					            logger.debug("Problem converting bytes to string - {}", e.getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return "";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the content of this <tt>FastByteArrayOutputStream</tt>
 | 
				
			||||||
 | 
					     * as String.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param encoding the encoding to be used for conversion.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a newly allocated String.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws UnsupportedEncodingException if the given encoding is not supported.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public String toString(String encoding) throws UnsupportedEncodingException {
 | 
				
			||||||
 | 
					        return new String(buf, 0, count, encoding);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the written bytes in a newly allocated byte[]
 | 
				
			||||||
 | 
					     * of length getSize().
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a newly allocated byte[] with the content of the
 | 
				
			||||||
 | 
					     * output buffer.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public byte[] toByteArray() {
 | 
				
			||||||
 | 
					        byte[] toBuf = new byte[count];
 | 
				
			||||||
 | 
					        System.arraycopy(buf, 0, toBuf, 0, count);
 | 
				
			||||||
 | 
					        //for (int i = 0; i < count; i++) {
 | 
				
			||||||
 | 
					        //  toBuf[i] = buf[i];
 | 
				
			||||||
 | 
					        //}
 | 
				
			||||||
 | 
					        return toBuf;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // --- end ByteArrayOutputStream compatible methods ---
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Copy the buffered data to the given array.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param toBuf  the buffer to hold a copy of the data.
 | 
				
			||||||
 | 
					     * @param offset the offset at which to start copying.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void toByteArray(byte[] toBuf, int offset) {
 | 
				
			||||||
 | 
					        int toLen = (toBuf.length > count) ? count : toBuf.length;
 | 
				
			||||||
 | 
					        System.arraycopy(buf, offset, toBuf, offset, toLen - offset);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the buffer owned by this object.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the buffer.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized byte[] getBufferBytes() {
 | 
				
			||||||
 | 
					        byte[] dest = new byte[count];
 | 
				
			||||||
 | 
					        System.arraycopy(buf, 0, dest, 0, dest.length);
 | 
				
			||||||
 | 
					        return dest;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the offset of the internal buffer.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return always zero currently.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getBufferOffset() {
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the length used in the internal buffer, that is, the offset at
 | 
				
			||||||
 | 
					     * which data will be written next.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the buffer length.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getBufferLength() {
 | 
				
			||||||
 | 
					        return count;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Ensure that at least the given number of bytes are available in the
 | 
				
			||||||
 | 
					     * internal buffer.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param sizeNeeded the number of bytes desired.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void makeSpace(int sizeNeeded) {
 | 
				
			||||||
 | 
					        int needed = count + sizeNeeded - buf.length;
 | 
				
			||||||
 | 
					        if (needed > 0) {
 | 
				
			||||||
 | 
					            bump(needed);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Skip the given number of bytes in the buffer.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param sizeAdded number of bytes to skip.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void addSize(int sizeAdded) {
 | 
				
			||||||
 | 
					        count += sizeAdded;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void bump(int needed) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        byte[] toBuf = new byte[buf.length + needed + bumpLen];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        System.arraycopy(buf, 0, toBuf, 0, count);
 | 
				
			||||||
 | 
					        buf = toBuf;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.io;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.ModbusIOException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ModbusRequest;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ModbusResponse;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.net.Socket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class that implements the ModbusRTU over tCP transport flavor.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author axuan
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ModbusRTUTCPTransport extends ModbusTCPTransport {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Default constructor
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusRTUTCPTransport() {
 | 
				
			||||||
 | 
					        // RTU over TCP is headless by default
 | 
				
			||||||
 | 
					        setHeadless();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ModbusTransport</tt> instance, for a given
 | 
				
			||||||
 | 
					     * <tt>Socket</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param socket the <tt>Socket</tt> used for message transport.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusRTUTCPTransport(Socket socket) {
 | 
				
			||||||
 | 
					        super(socket);
 | 
				
			||||||
 | 
					        // RTU over TCP is headless by default
 | 
				
			||||||
 | 
					        setHeadless();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void writeResponse(ModbusResponse msg) throws ModbusIOException {
 | 
				
			||||||
 | 
					        writeMessage(msg, true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void writeRequest(ModbusRequest msg) throws ModbusIOException {
 | 
				
			||||||
 | 
					        writeMessage(msg, true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,247 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.io;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.ModbusException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.ModbusIOException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.ModbusSlaveException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ExceptionResponse;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ModbusRequest;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.TCPMasterConnection;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.util.ModbusUtil;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing the <tt>ModbusTransaction</tt> interface.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ModbusTCPTransaction extends ModbusTransaction {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final Logger logger = LoggerFactory.getLogger(ModbusTCPTransaction.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // instance attributes and associations
 | 
				
			||||||
 | 
					    private TCPMasterConnection connection;
 | 
				
			||||||
 | 
					    protected boolean reconnecting = Modbus.DEFAULT_RECONNECTING;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ModbusTCPTransaction</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusTCPTransaction() {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ModbusTCPTransaction</tt> instance with a given
 | 
				
			||||||
 | 
					     * <tt>ModbusRequest</tt> to be send when the transaction is executed.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param request a <tt>ModbusRequest</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusTCPTransaction(ModbusRequest request) {
 | 
				
			||||||
 | 
					        setRequest(request);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ModbusTCPTransaction</tt> instance with a given
 | 
				
			||||||
 | 
					     * <tt>TCPMasterConnection</tt> to be used for transactions.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param con a <tt>TCPMasterConnection</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusTCPTransaction(TCPMasterConnection con) {
 | 
				
			||||||
 | 
					        setConnection(con);
 | 
				
			||||||
 | 
					        transport = con.getModbusTransport();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the connection on which this <tt>ModbusTransaction</tt> should be
 | 
				
			||||||
 | 
					     * executed.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * An implementation should be able to handle open and closed connections.
 | 
				
			||||||
 | 
					     * <br>
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param con a <tt>TCPMasterConnection</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized void setConnection(TCPMasterConnection con) {
 | 
				
			||||||
 | 
					        connection = con;
 | 
				
			||||||
 | 
					        transport = con.getModbusTransport();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Tests if the connection will be opened and closed for <b>each</b>
 | 
				
			||||||
 | 
					     * execution.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return true if reconnecting, false otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean isReconnecting() {
 | 
				
			||||||
 | 
					        return reconnecting;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the flag that controls whether a connection is opened and closed
 | 
				
			||||||
 | 
					     * for <b>each</b> execution or not.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param b true if reconnecting, false otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setReconnecting(boolean b) {
 | 
				
			||||||
 | 
					        reconnecting = b;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public synchronized void execute() throws ModbusException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (request == null || connection == null) {
 | 
				
			||||||
 | 
					            throw new ModbusException("Invalid request or connection");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Try sending the message up to retries time. Note that the message
 | 
				
			||||||
 | 
					        // is read immediately after being written, with no flushing of buffers.
 | 
				
			||||||
 | 
					        int retryCounter = 0;
 | 
				
			||||||
 | 
					        int retryLimit = (retries > 0 ? retries : Modbus.DEFAULT_RETRIES);
 | 
				
			||||||
 | 
					        boolean keepTrying = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // While we haven't exhausted all the retry attempts
 | 
				
			||||||
 | 
					        while (keepTrying) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Automatically connect if we aren't already connected
 | 
				
			||||||
 | 
					            if (!connection.isConnected()) {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    logger.debug("Connecting to: {}:{}", connection.getAddress().toString(), connection.getPort());
 | 
				
			||||||
 | 
					                    connection.connect();
 | 
				
			||||||
 | 
					                    transport = connection.getModbusTransport();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                catch (Exception ex) {
 | 
				
			||||||
 | 
					                    throw new ModbusIOException("Connection failed for %s:%d", connection.getAddress().toString(), connection.getPort(), ex.getMessage());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Make sure the timeout is set
 | 
				
			||||||
 | 
					            transport.setTimeout(connection.getTimeout());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Write the message to the endpoint
 | 
				
			||||||
 | 
					                logger.debug("Writing request: {} (try: {}) request transaction ID = {} to {}:{}", request.getHexMessage(), retryCounter, request.getTransactionID(), connection.getAddress().toString(), connection.getPort());
 | 
				
			||||||
 | 
					                transport.writeRequest(request);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Read the response
 | 
				
			||||||
 | 
					                response = transport.readResponse();
 | 
				
			||||||
 | 
					                logger.debug("Read response: {} (try: {}) response transaction ID = {} from {}:{}", response.getHexMessage(), retryCounter, response.getTransactionID(), connection.getAddress().toString(), connection.getPort());
 | 
				
			||||||
 | 
					                keepTrying = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // The slave may have returned an exception -- check for that.
 | 
				
			||||||
 | 
					                if (response instanceof ExceptionResponse) {
 | 
				
			||||||
 | 
					                    throw new ModbusSlaveException(((ExceptionResponse)response).getExceptionCode());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // We need to keep retrying if;
 | 
				
			||||||
 | 
					                //   a) the response is empty OR
 | 
				
			||||||
 | 
					                //   b) we have been told to check the validity and the request/response transaction IDs don't match AND
 | 
				
			||||||
 | 
					                //   c) we haven't exceeded the maximum retry count
 | 
				
			||||||
 | 
					                if (responseIsInValid()) {
 | 
				
			||||||
 | 
					                    retryCounter++;
 | 
				
			||||||
 | 
					                    if (retryCounter >= retryLimit) {
 | 
				
			||||||
 | 
					                        throw new ModbusIOException("Executing transaction failed (tried %d times)", retryLimit);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    keepTrying = true;
 | 
				
			||||||
 | 
					                    long sleepTime = getRandomSleepTime(retryCounter);
 | 
				
			||||||
 | 
					                    if (response == null) {
 | 
				
			||||||
 | 
					                        logger.debug("Failed to get any response (try: {}) - retrying after {} milliseconds", retryCounter, sleepTime);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    else {
 | 
				
			||||||
 | 
					                        logger.debug("Failed to get a valid response, transaction IDs do not match (try: {}) - retrying after {} milliseconds", retryCounter, sleepTime);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    ModbusUtil.sleep(sleepTime);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (ModbusIOException ex) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Up the retry counter and check if we are exhausted
 | 
				
			||||||
 | 
					                retryCounter++;
 | 
				
			||||||
 | 
					                if (retryCounter >= retryLimit) {
 | 
				
			||||||
 | 
					                    throw new ModbusIOException("Executing transaction %s failed (tried %d times) %s", request.getHexMessage(), retryLimit, ex.getMessage());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else {
 | 
				
			||||||
 | 
					                    long sleepTime = getRandomSleepTime(retryCounter);
 | 
				
			||||||
 | 
					                    logger.debug("Failed transaction Request: {} (try: {}) - retrying after {} milliseconds", request.getHexMessage(), retryCounter, sleepTime);
 | 
				
			||||||
 | 
					                    ModbusUtil.sleep(sleepTime);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // If this has happened, then we should close and re-open the connection before re-trying
 | 
				
			||||||
 | 
					                logger.debug("Failed request {} (try: {}) request transaction ID = {} - {} closing and re-opening connection {}:{}", request.getHexMessage(), retryCounter, request.getTransactionID(), ex.getMessage(), connection.getAddress().toString(), connection.getPort());
 | 
				
			||||||
 | 
					                connection.close();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Increment the transaction ID if we are still trying
 | 
				
			||||||
 | 
					            if (keepTrying) {
 | 
				
			||||||
 | 
					                incrementTransactionID();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Close the connection if it isn't supposed to stick around.
 | 
				
			||||||
 | 
					        if (isReconnecting()) {
 | 
				
			||||||
 | 
					            connection.close();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        incrementTransactionID();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns true if the response is not valid
 | 
				
			||||||
 | 
					     * This can be if the response is null or the transaction ID of the request
 | 
				
			||||||
 | 
					     * doesn't match the reponse
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return True if invalid
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private boolean responseIsInValid() {
 | 
				
			||||||
 | 
					        if (response == null) {
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if (!response.isHeadless() && validityCheck) {
 | 
				
			||||||
 | 
					            return request.getTransactionID() != response.getTransactionID();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * incrementTransactionID -- Increment the transaction ID for the next
 | 
				
			||||||
 | 
					     * transaction. Note that the caller must get the new transaction ID with
 | 
				
			||||||
 | 
					     * getTransactionID(). This is only done validity checking is enabled so
 | 
				
			||||||
 | 
					     * that dumb slaves don't cause problems. The original request will have its
 | 
				
			||||||
 | 
					     * transaction ID incremented as well so that sending the same transaction
 | 
				
			||||||
 | 
					     * again won't cause problems.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private synchronized void incrementTransactionID() {
 | 
				
			||||||
 | 
					        if (isCheckingValidity()) {
 | 
				
			||||||
 | 
					            if (transactionID >= Modbus.MAX_TRANSACTION_ID) {
 | 
				
			||||||
 | 
					                transactionID = Modbus.DEFAULT_TRANSACTION_ID;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else {
 | 
				
			||||||
 | 
					                transactionID++;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        request.setTransactionID(getTransactionID());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,407 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.io;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.ModbusIOException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ModbusMessage;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ModbusRequest;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ModbusResponse;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractModbusListener;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.TCPMasterConnection;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.util.ModbusUtil;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.*;
 | 
				
			||||||
 | 
					import java.net.Socket;
 | 
				
			||||||
 | 
					import java.net.SocketException;
 | 
				
			||||||
 | 
					import java.net.SocketTimeoutException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class that implements the Modbus transport flavor.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ModbusTCPTransport extends AbstractModbusTransport {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final Logger logger = LoggerFactory.getLogger(ModbusTCPTransport.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // instance attributes
 | 
				
			||||||
 | 
					    private DataInputStream dataInputStream; // input stream
 | 
				
			||||||
 | 
					    private DataOutputStream dataOutputStream; // output stream
 | 
				
			||||||
 | 
					    private final BytesInputStream byteInputStream = new BytesInputStream(Modbus.MAX_MESSAGE_LENGTH + 6);
 | 
				
			||||||
 | 
					    private final BytesOutputStream byteOutputStream = new BytesOutputStream(Modbus.MAX_MESSAGE_LENGTH + 6); // write frames
 | 
				
			||||||
 | 
					    protected Socket socket = null;
 | 
				
			||||||
 | 
					    protected TCPMasterConnection master = null;
 | 
				
			||||||
 | 
					    private boolean headless = false; // Some TCP implementations are.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Default constructor
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusTCPTransport() {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ModbusTransport</tt> instance, for a given
 | 
				
			||||||
 | 
					     * <tt>Socket</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param socket the <tt>Socket</tt> used for message transport.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusTCPTransport(Socket socket) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            setSocket(socket);
 | 
				
			||||||
 | 
					            socket.setSoTimeout(timeout);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (IOException ex) {
 | 
				
			||||||
 | 
					            logger.debug("ModbusTCPTransport::Socket invalid");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            throw new IllegalStateException("Socket invalid", ex);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the <tt>Socket</tt> used for message transport and prepares the
 | 
				
			||||||
 | 
					     * streams used for the actual I/O.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param socket the <tt>Socket</tt> used for message transport.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IOException if an I/O related error occurs.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setSocket(Socket socket) throws IOException {
 | 
				
			||||||
 | 
					        if (this.socket != null) {
 | 
				
			||||||
 | 
					            this.socket.close();
 | 
				
			||||||
 | 
					            this.socket = null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this.socket = socket;
 | 
				
			||||||
 | 
					        setTimeout(timeout);
 | 
				
			||||||
 | 
					        prepareStreams(socket);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Set the transport to be headless
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setHeadless() {
 | 
				
			||||||
 | 
					        headless = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Set the transport to be headless
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param headless True if headless
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setHeadless(boolean headless) {
 | 
				
			||||||
 | 
					        this.headless = headless;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the master connection for the transport to use
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param master Master
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setMaster(TCPMasterConnection master) {
 | 
				
			||||||
 | 
					        this.master = master;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void setTimeout(int time) {
 | 
				
			||||||
 | 
					        super.setTimeout(time);
 | 
				
			||||||
 | 
					        if (socket != null) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                socket.setSoTimeout(time);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (SocketException e) {
 | 
				
			||||||
 | 
					                logger.warn("Socket exception occurred while setting timeout to " + time, e);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void close() throws IOException {
 | 
				
			||||||
 | 
					        dataInputStream.close();
 | 
				
			||||||
 | 
					        dataOutputStream.close();
 | 
				
			||||||
 | 
					        socket.close();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusTransaction createTransaction() {
 | 
				
			||||||
 | 
					        if (master == null) {
 | 
				
			||||||
 | 
					            master = new TCPMasterConnection(socket.getInetAddress());
 | 
				
			||||||
 | 
					            master.setPort(socket.getPort());
 | 
				
			||||||
 | 
					            master.setModbusTransport(this);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return new ModbusTCPTransaction(master);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void writeResponse(ModbusResponse msg) throws ModbusIOException {
 | 
				
			||||||
 | 
					        writeMessage(msg, false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void writeRequest(ModbusRequest msg) throws ModbusIOException {
 | 
				
			||||||
 | 
					        writeMessage(msg, false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusRequest readRequest(AbstractModbusListener listener) throws ModbusIOException {
 | 
				
			||||||
 | 
					        ModbusRequest req;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            byteInputStream.reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            synchronized (byteInputStream) {
 | 
				
			||||||
 | 
					                byte[] buffer = byteInputStream.getBuffer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (!headless) {
 | 
				
			||||||
 | 
					                    dataInputStream.readFully(buffer, 0, 6);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // The transaction ID must be treated as an unsigned short in
 | 
				
			||||||
 | 
					                    // order for validation to work correctly.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    int transaction = ModbusUtil.registerToShort(buffer, 0) & 0x0000FFFF;
 | 
				
			||||||
 | 
					                    int protocol = ModbusUtil.registerToShort(buffer, 2);
 | 
				
			||||||
 | 
					                    int count = ModbusUtil.registerToShort(buffer, 4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    dataInputStream.readFully(buffer, 6, count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (logger.isDebugEnabled()) {
 | 
				
			||||||
 | 
					                        logger.debug("Read: {}", ModbusUtil.toHex(buffer, 0, count + 6));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    byteInputStream.reset(buffer, (6 + count));
 | 
				
			||||||
 | 
					                    byteInputStream.skip(6);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    int unit = byteInputStream.readByte();
 | 
				
			||||||
 | 
					                    int functionCode = byteInputStream.readUnsignedByte();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    byteInputStream.reset();
 | 
				
			||||||
 | 
					                    req = ModbusRequest.createModbusRequest(functionCode);
 | 
				
			||||||
 | 
					                    req.setUnitID(unit);
 | 
				
			||||||
 | 
					                    req.setHeadless(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    req.setTransactionID(transaction);
 | 
				
			||||||
 | 
					                    req.setProtocolID(protocol);
 | 
				
			||||||
 | 
					                    req.setDataLength(count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    req.readFrom(byteInputStream);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // This is a headless request.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    int unit = dataInputStream.readByte();
 | 
				
			||||||
 | 
					                    int function = dataInputStream.readByte();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    req = ModbusRequest.createModbusRequest(function);
 | 
				
			||||||
 | 
					                    req.setUnitID(unit);
 | 
				
			||||||
 | 
					                    req.setHeadless(true);
 | 
				
			||||||
 | 
					                    req.readData(dataInputStream);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Discard the CRC. This is a TCP/IP connection, which has
 | 
				
			||||||
 | 
					                    // proper error correction and recovery.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    dataInputStream.readShort();
 | 
				
			||||||
 | 
					                    if (logger.isDebugEnabled()) {
 | 
				
			||||||
 | 
					                        logger.debug("Read: {}", req.getHexMessage());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return req;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (EOFException eoex) {
 | 
				
			||||||
 | 
					            throw new ModbusIOException("End of File", true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (SocketTimeoutException x) {
 | 
				
			||||||
 | 
					            throw new ModbusIOException("Timeout reading request", x);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (SocketException sockex) {
 | 
				
			||||||
 | 
					            throw new ModbusIOException("Socket Exception", sockex);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (IOException ex) {
 | 
				
			||||||
 | 
					            throw new ModbusIOException("I/O exception - failed to read", ex);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse readResponse() throws ModbusIOException {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            ModbusResponse response;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            synchronized (byteInputStream) {
 | 
				
			||||||
 | 
					                // use same buffer
 | 
				
			||||||
 | 
					                byte[] buffer = byteInputStream.getBuffer();
 | 
				
			||||||
 | 
					                logger.debug("Reading response...");
 | 
				
			||||||
 | 
					                if (!headless) {
 | 
				
			||||||
 | 
					                    // All Modbus TCP transactions start with 6 bytes. Get them.
 | 
				
			||||||
 | 
					                    dataInputStream.readFully(buffer, 0, 6);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    /*
 | 
				
			||||||
 | 
					                     * The transaction ID is the first word (offset 0) in the
 | 
				
			||||||
 | 
					                     * data that was just read. It will be echoed back to the
 | 
				
			||||||
 | 
					                     * requester.
 | 
				
			||||||
 | 
					                     *
 | 
				
			||||||
 | 
					                     * The protocol ID is the second word (offset 2) in the
 | 
				
			||||||
 | 
					                     * data. It should always be 0, but I don't check.
 | 
				
			||||||
 | 
					                     *
 | 
				
			||||||
 | 
					                     * The length of the payload is the third word (offset 4) in
 | 
				
			||||||
 | 
					                     * the data that was just read. That's what I need in order
 | 
				
			||||||
 | 
					                     * to read the rest of the response.
 | 
				
			||||||
 | 
					                     */
 | 
				
			||||||
 | 
					                    int transaction = ModbusUtil.registerToShort(buffer, 0) & 0x0000FFFF;
 | 
				
			||||||
 | 
					                    int protocol = ModbusUtil.registerToShort(buffer, 2);
 | 
				
			||||||
 | 
					                    int count = ModbusUtil.registerToShort(buffer, 4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    dataInputStream.readFully(buffer, 6, count);
 | 
				
			||||||
 | 
					                    byteInputStream.reset(buffer, (6 + count));
 | 
				
			||||||
 | 
					                    byteInputStream.reset();
 | 
				
			||||||
 | 
					                    byteInputStream.skip(7);
 | 
				
			||||||
 | 
					                    int function = byteInputStream.readUnsignedByte();
 | 
				
			||||||
 | 
					                    response = ModbusResponse.createModbusResponse(function);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Rewind the input buffer, then read the data into the
 | 
				
			||||||
 | 
					                    // response.
 | 
				
			||||||
 | 
					                    byteInputStream.reset();
 | 
				
			||||||
 | 
					                    response.readFrom(byteInputStream);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    response.setTransactionID(transaction);
 | 
				
			||||||
 | 
					                    response.setProtocolID(protocol);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else {
 | 
				
			||||||
 | 
					                    // This is a headless response. It has the same format as a
 | 
				
			||||||
 | 
					                    // RTU over Serial response.
 | 
				
			||||||
 | 
					                    int unit = dataInputStream.readByte();
 | 
				
			||||||
 | 
					                    int function = dataInputStream.readByte();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    response = ModbusResponse.createModbusResponse(function);
 | 
				
			||||||
 | 
					                    response.setUnitID(unit);
 | 
				
			||||||
 | 
					                    response.setHeadless();
 | 
				
			||||||
 | 
					                    response.readData(dataInputStream);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Now discard the CRC. Which hopefully wasn't needed
 | 
				
			||||||
 | 
					                    // because this is a TCP transport.
 | 
				
			||||||
 | 
					                    dataInputStream.readShort();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (logger.isDebugEnabled()) {
 | 
				
			||||||
 | 
					                logger.debug("Successfully read: {}", response.getHexMessage());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return response;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (EOFException ex1) {
 | 
				
			||||||
 | 
					            throw new ModbusIOException("Premature end of stream (Message truncated) - %s", ex1.getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (SocketTimeoutException ex2) {
 | 
				
			||||||
 | 
					            throw new ModbusIOException("Socket timeout reading response - %s", ex2.getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (Exception ex3) {
 | 
				
			||||||
 | 
					            throw new ModbusIOException("General exception - failed to read - %s", ex3.getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Prepares the input and output streams of this <tt>ModbusTCPTransport</tt>
 | 
				
			||||||
 | 
					     * instance based on the given socket.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param socket the socket used for communications.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IOException if an I/O related error occurs.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void prepareStreams(Socket socket) throws IOException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Close any open streams if I'm being called because a new socket was
 | 
				
			||||||
 | 
					        // set to handle this transport.
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (dataInputStream != null) {
 | 
				
			||||||
 | 
					                dataInputStream.close();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (dataOutputStream != null) {
 | 
				
			||||||
 | 
					                dataOutputStream.close();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (IOException x) {
 | 
				
			||||||
 | 
					            // Do nothing.
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dataInputStream = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
 | 
				
			||||||
 | 
					        dataOutputStream = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Writes a <tt>ModbusMessage</tt> to the
 | 
				
			||||||
 | 
					     * output stream of this <tt>ModbusTransport</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param msg           a <tt>ModbusMessage</tt>.
 | 
				
			||||||
 | 
					     * @param useRtuOverTcp True if the RTU protocol should be used over TCP
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws ModbusIOException data cannot be
 | 
				
			||||||
 | 
					     *                           written properly to the raw output stream of
 | 
				
			||||||
 | 
					     *                           this <tt>ModbusTransport</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void writeMessage(ModbusMessage msg, boolean useRtuOverTcp) throws ModbusIOException {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (logger.isDebugEnabled()) {
 | 
				
			||||||
 | 
					                logger.debug("Sending: {}", msg.getHexMessage());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            byte message[] = msg.getMessage();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            byteOutputStream.reset();
 | 
				
			||||||
 | 
					            if (!headless) {
 | 
				
			||||||
 | 
					                byteOutputStream.writeShort(msg.getTransactionID());
 | 
				
			||||||
 | 
					                byteOutputStream.writeShort(msg.getProtocolID());
 | 
				
			||||||
 | 
					                byteOutputStream.writeShort((message != null ? message.length : 0) + 2);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            byteOutputStream.writeByte(msg.getUnitID());
 | 
				
			||||||
 | 
					            byteOutputStream.writeByte(msg.getFunctionCode());
 | 
				
			||||||
 | 
					            if (message != null && message.length > 0) {
 | 
				
			||||||
 | 
					                byteOutputStream.write(message);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Add CRC for RTU over TCP
 | 
				
			||||||
 | 
					            if (useRtuOverTcp) {
 | 
				
			||||||
 | 
					                int len = byteOutputStream.size();
 | 
				
			||||||
 | 
					                int[] crc = ModbusUtil.calculateCRC(byteOutputStream.getBuffer(), 0, len);
 | 
				
			||||||
 | 
					                byteOutputStream.writeByte(crc[0]);
 | 
				
			||||||
 | 
					                byteOutputStream.writeByte(crc[1]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            dataOutputStream.write(byteOutputStream.toByteArray());
 | 
				
			||||||
 | 
					            dataOutputStream.flush();
 | 
				
			||||||
 | 
					            if (logger.isDebugEnabled()) {
 | 
				
			||||||
 | 
					                logger.debug("Successfully sent: {}", ModbusUtil.toHex(byteOutputStream.toByteArray()));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            // write more sophisticated exception handling
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (SocketException ex1) {
 | 
				
			||||||
 | 
					            if (master != null && !master.isConnected()) {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    master.connect(useRtuOverTcp);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                catch (Exception e) {
 | 
				
			||||||
 | 
					                    // Do nothing.
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            throw new ModbusIOException("I/O socket exception - failed to write - %s", ex1.getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (Exception ex2) {
 | 
				
			||||||
 | 
					            throw new ModbusIOException("General exception - failed to write - %s", ex2.getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,171 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.io;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.ModbusException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ModbusRequest;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ModbusResponse;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Random;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Interface defining a ModbusTransaction.
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * A transaction is defined by the sequence of
 | 
				
			||||||
 | 
					 * sending a request message and receiving a
 | 
				
			||||||
 | 
					 * related response message.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public abstract class ModbusTransaction {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected AbstractModbusTransport transport;
 | 
				
			||||||
 | 
					    protected ModbusRequest request;
 | 
				
			||||||
 | 
					    protected ModbusResponse response;
 | 
				
			||||||
 | 
					    boolean validityCheck = Modbus.DEFAULT_VALIDITYCHECK;
 | 
				
			||||||
 | 
					    int retries = Modbus.DEFAULT_RETRIES;
 | 
				
			||||||
 | 
					    private Random random = new Random(System.nanoTime());
 | 
				
			||||||
 | 
					    static int transactionID = Modbus.DEFAULT_TRANSACTION_ID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the <tt>ModbusRequest</tt> instance
 | 
				
			||||||
 | 
					     * associated with this <tt>ModbusTransaction</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the associated <tt>ModbusRequest</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusRequest getRequest() {
 | 
				
			||||||
 | 
					        return request;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the <tt>ModbusRequest</tt> for this
 | 
				
			||||||
 | 
					     * <tt>ModbusTransaction</tt>.<p>
 | 
				
			||||||
 | 
					     * The related <tt>ModbusResponse</tt> is acquired
 | 
				
			||||||
 | 
					     * from the passed in <tt>ModbusRequest</tt> instance.<br>
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param req a <tt>ModbusRequest</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setRequest(ModbusRequest req) {
 | 
				
			||||||
 | 
					        request = req;
 | 
				
			||||||
 | 
					        if (req != null) {
 | 
				
			||||||
 | 
					            request.setTransactionID(getTransactionID());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the <tt>ModbusResponse</tt> instance
 | 
				
			||||||
 | 
					     * associated with this <tt>ModbusTransaction</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the associated <tt>ModbusRequest</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusResponse getResponse() {
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the amount of retries for opening
 | 
				
			||||||
 | 
					     * the connection for executing the transaction.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the amount of retries as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int getRetries() {
 | 
				
			||||||
 | 
					        return retries;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Set the amount of retries for opening
 | 
				
			||||||
 | 
					     * the connection for executing the transaction.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param retries the amount of retries as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setRetries(int retries) {
 | 
				
			||||||
 | 
					        this.retries = retries;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Tests whether the validity of a transaction
 | 
				
			||||||
 | 
					     * will be checked.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return true if checking validity, false otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean isCheckingValidity() {
 | 
				
			||||||
 | 
					        return validityCheck;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the flag that controls whether the
 | 
				
			||||||
 | 
					     * validity of a transaction will be checked.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param b true if checking validity, false otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setCheckingValidity(boolean b) {
 | 
				
			||||||
 | 
					        validityCheck = b;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getTransactionID -- get the next transaction ID to use.
 | 
				
			||||||
 | 
					     * @return next transaction ID to use
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    synchronized public int getTransactionID() {
 | 
				
			||||||
 | 
					        /*
 | 
				
			||||||
 | 
					         * Ensure that the transaction ID is in the valid range between
 | 
				
			||||||
 | 
					         * 0 and MAX_TRANSACTION_ID (65534).  If not, the value will be forced
 | 
				
			||||||
 | 
					         * to 0.
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        if (transactionID < Modbus.DEFAULT_TRANSACTION_ID && isCheckingValidity()) {
 | 
				
			||||||
 | 
					            transactionID = Modbus.DEFAULT_TRANSACTION_ID;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (transactionID >= Modbus.MAX_TRANSACTION_ID) {
 | 
				
			||||||
 | 
					            transactionID = Modbus.DEFAULT_TRANSACTION_ID;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return transactionID;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * A useful method for getting a random sleep time based on an increment of the retry count and retry sleep time
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param count Retry count
 | 
				
			||||||
 | 
					     * @return Random sleep time in milliseconds
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    long getRandomSleepTime(int count) {
 | 
				
			||||||
 | 
					        return (Modbus.RETRY_SLEEP_TIME / 2) + (long) (random.nextDouble() * Modbus.RETRY_SLEEP_TIME * count);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Executes this <tt>ModbusTransaction</tt>.
 | 
				
			||||||
 | 
					     * Locks the <tt>ModbusTransport</tt> for sending
 | 
				
			||||||
 | 
					     * the <tt>ModbusRequest</tt> and reading the
 | 
				
			||||||
 | 
					     * related <tt>ModbusResponse</tt>.
 | 
				
			||||||
 | 
					     * If reconnecting is activated the connection will
 | 
				
			||||||
 | 
					     * be opened for the transaction and closed afterwards.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws ModbusException if an I/O error occurs,
 | 
				
			||||||
 | 
					     *                         or the response is a modbus protocol exception.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract void execute() throws ModbusException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,185 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.io;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.ModbusException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.ModbusIOException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.ModbusSlaveException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ExceptionResponse;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ModbusRequest;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractUDPTerminal;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.UDPMasterConnection;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.util.ModbusUtil;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing the <tt>ModbusTransaction</tt>
 | 
				
			||||||
 | 
					 * interface for the UDP transport mechanism.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ModbusUDPTransaction extends ModbusTransaction {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final Logger logger = LoggerFactory.getLogger(ModbusUDPTransaction.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //instance attributes and associations
 | 
				
			||||||
 | 
					    private AbstractUDPTerminal terminal;
 | 
				
			||||||
 | 
					    private final Object MUTEX = new Object();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ModbusUDPTransaction</tt>
 | 
				
			||||||
 | 
					     * instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusUDPTransaction() {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ModbusUDPTransaction</tt>
 | 
				
			||||||
 | 
					     * instance with a given <tt>ModbusRequest</tt> to
 | 
				
			||||||
 | 
					     * be send when the transaction is executed.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param request a <tt>ModbusRequest</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusUDPTransaction(ModbusRequest request) {
 | 
				
			||||||
 | 
					        setRequest(request);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ModbusUDPTransaction</tt>
 | 
				
			||||||
 | 
					     * instance with a given <tt>UDPTerminal</tt> to
 | 
				
			||||||
 | 
					     * be used for transactions.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param terminal a <tt>UDPTerminal</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusUDPTransaction(AbstractUDPTerminal terminal) {
 | 
				
			||||||
 | 
					        setTerminal(terminal);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ModbusUDPTransaction</tt>
 | 
				
			||||||
 | 
					     * instance with a given <tt>ModbusUDPConnection</tt>
 | 
				
			||||||
 | 
					     * to be used for transactions.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param con a <tt>ModbusUDPConnection</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusUDPTransaction(UDPMasterConnection con) {
 | 
				
			||||||
 | 
					        setTerminal(con.getTerminal());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the terminal on which this <tt>ModbusTransaction</tt>
 | 
				
			||||||
 | 
					     * should be executed.<p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param terminal a <tt>UDPSlaveTerminal</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setTerminal(AbstractUDPTerminal terminal) {
 | 
				
			||||||
 | 
					        this.terminal = terminal;
 | 
				
			||||||
 | 
					        if (terminal.isActive()) {
 | 
				
			||||||
 | 
					            transport = terminal.getTransport();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void execute() throws ModbusIOException, ModbusSlaveException, ModbusException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //1. assert executeability
 | 
				
			||||||
 | 
					        assertExecutable();
 | 
				
			||||||
 | 
					        //2. open the connection if not connected
 | 
				
			||||||
 | 
					        if (!terminal.isActive()) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                terminal.activate();
 | 
				
			||||||
 | 
					                transport = terminal.getTransport();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (Exception ex) {
 | 
				
			||||||
 | 
					                logger.debug("Terminal activation failed.", ex);
 | 
				
			||||||
 | 
					                throw new ModbusIOException("Activation failed");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //3. Retry transaction retries times, in case of
 | 
				
			||||||
 | 
					        //I/O Exception problems.
 | 
				
			||||||
 | 
					        int retryCount = 0;
 | 
				
			||||||
 | 
					        while (retryCount <= retries) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                //3. write request, and read response,
 | 
				
			||||||
 | 
					                //   while holding the lock on the IO object
 | 
				
			||||||
 | 
					                synchronized (MUTEX) {
 | 
				
			||||||
 | 
					                    //write request message
 | 
				
			||||||
 | 
					                    transport.writeRequest(request);
 | 
				
			||||||
 | 
					                    //read response message
 | 
				
			||||||
 | 
					                    response = transport.readResponse();
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (ModbusIOException ex) {
 | 
				
			||||||
 | 
					                retryCount++;
 | 
				
			||||||
 | 
					                if (retryCount > retries) {
 | 
				
			||||||
 | 
					                    logger.error("Cannot send UDP message", ex);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else {
 | 
				
			||||||
 | 
					                    ModbusUtil.sleep(getRandomSleepTime(retryCount));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //4. deal with "application level" exceptions
 | 
				
			||||||
 | 
					        if (response instanceof ExceptionResponse) {
 | 
				
			||||||
 | 
					            throw new ModbusSlaveException(((ExceptionResponse)response).getExceptionCode());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //toggle the id
 | 
				
			||||||
 | 
					        incrementTransactionID();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Asserts if this <tt>ModbusTCPTransaction</tt> is
 | 
				
			||||||
 | 
					     * executable.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws ModbusException if this transaction cannot be
 | 
				
			||||||
 | 
					     *                         asserted as executable.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void assertExecutable() throws ModbusException {
 | 
				
			||||||
 | 
					        if (request == null || terminal == null) {
 | 
				
			||||||
 | 
					            throw new ModbusException("Assertion failed, transaction not executable");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Toggles the transaction identifier, to ensure
 | 
				
			||||||
 | 
					     * that each transaction has a distinctive
 | 
				
			||||||
 | 
					     * identifier.<br>
 | 
				
			||||||
 | 
					     * When the maximum value of 65535 has been reached,
 | 
				
			||||||
 | 
					     * the identifiers will start from zero again.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void incrementTransactionID() {
 | 
				
			||||||
 | 
					        if (isCheckingValidity()) {
 | 
				
			||||||
 | 
					            if (transactionID >= Modbus.MAX_TRANSACTION_ID) {
 | 
				
			||||||
 | 
					                transactionID = Modbus.DEFAULT_TRANSACTION_ID;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else {
 | 
				
			||||||
 | 
					                transactionID++;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        request.setTransactionID(getTransactionID());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,152 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.io;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.ModbusIOException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ModbusMessage;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ModbusRequest;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ModbusResponse;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractModbusListener;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractUDPTerminal;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.io.InterruptedIOException;
 | 
				
			||||||
 | 
					import java.util.Arrays;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class that implements the Modbus UDP transport flavor.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ModbusUDPTransport extends AbstractModbusTransport {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final Logger logger = LoggerFactory.getLogger(ModbusUDPTransport.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //instance attributes
 | 
				
			||||||
 | 
					    private AbstractUDPTerminal terminal;
 | 
				
			||||||
 | 
					    private final BytesOutputStream byteOutputStream = new BytesOutputStream(Modbus.MAX_MESSAGE_LENGTH);
 | 
				
			||||||
 | 
					    private final BytesInputStream byteInputStream = new BytesInputStream(Modbus.MAX_MESSAGE_LENGTH);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ModbusTransport</tt> instance,
 | 
				
			||||||
 | 
					     * for a given <tt>UDPTerminal</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param terminal the <tt>UDPTerminal</tt> used for message transport.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusUDPTransport(AbstractUDPTerminal terminal) {
 | 
				
			||||||
 | 
					        this.terminal = terminal;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void setTimeout(int time) {
 | 
				
			||||||
 | 
					        super.setTimeout(time);
 | 
				
			||||||
 | 
					        if (terminal != null) {
 | 
				
			||||||
 | 
					            terminal.setTimeout(timeout);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void close() throws IOException {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusTransaction createTransaction() {
 | 
				
			||||||
 | 
					        ModbusUDPTransaction trans = new ModbusUDPTransaction();
 | 
				
			||||||
 | 
					        trans.setTerminal(terminal);
 | 
				
			||||||
 | 
					        return trans;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void writeResponse(ModbusResponse msg) throws ModbusIOException {
 | 
				
			||||||
 | 
					        writeMessage(msg);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void writeRequest(ModbusRequest msg) throws ModbusIOException {
 | 
				
			||||||
 | 
					        writeMessage(msg);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusRequest readRequest(AbstractModbusListener listener) throws ModbusIOException {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            ModbusRequest req;
 | 
				
			||||||
 | 
					            synchronized (byteInputStream) {
 | 
				
			||||||
 | 
					                byteInputStream.reset(terminal.receiveMessage());
 | 
				
			||||||
 | 
					                byteInputStream.skip(7);
 | 
				
			||||||
 | 
					                int functionCode = byteInputStream.readUnsignedByte();
 | 
				
			||||||
 | 
					                byteInputStream.reset();
 | 
				
			||||||
 | 
					                req = ModbusRequest.createModbusRequest(functionCode);
 | 
				
			||||||
 | 
					                req.readFrom(byteInputStream);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return req;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (Exception ex) {
 | 
				
			||||||
 | 
					            throw new ModbusIOException("I/O exception - failed to read", ex);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse readResponse() throws ModbusIOException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            ModbusResponse res;
 | 
				
			||||||
 | 
					            synchronized (byteInputStream) {
 | 
				
			||||||
 | 
					                byteInputStream.reset(terminal.receiveMessage());
 | 
				
			||||||
 | 
					                byteInputStream.skip(7);
 | 
				
			||||||
 | 
					                int functionCode = byteInputStream.readUnsignedByte();
 | 
				
			||||||
 | 
					                byteInputStream.reset();
 | 
				
			||||||
 | 
					                res = ModbusResponse.createModbusResponse(functionCode);
 | 
				
			||||||
 | 
					                res.readFrom(byteInputStream);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return res;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (InterruptedIOException ioex) {
 | 
				
			||||||
 | 
					            throw new ModbusIOException("Socket was interrupted", ioex);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (Exception ex) {
 | 
				
			||||||
 | 
					            logger.debug("I/O exception while reading modbus response.", ex);
 | 
				
			||||||
 | 
					            throw new ModbusIOException("I/O exception - failed to read - %s", ex.getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Writes the request/response message to the port
 | 
				
			||||||
 | 
					     * @param msg Message to write
 | 
				
			||||||
 | 
					     * @throws ModbusIOException If the port cannot be written to
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void writeMessage(ModbusMessage msg) throws ModbusIOException {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            synchronized (byteOutputStream) {
 | 
				
			||||||
 | 
					                int len = msg.getOutputLength();
 | 
				
			||||||
 | 
					                byteOutputStream.reset();
 | 
				
			||||||
 | 
					                msg.writeTo(byteOutputStream);
 | 
				
			||||||
 | 
					                byte data[] = byteOutputStream.getBuffer();
 | 
				
			||||||
 | 
					                data = Arrays.copyOf(data, len);
 | 
				
			||||||
 | 
					                terminal.sendMessage(data);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (Exception ex) {
 | 
				
			||||||
 | 
					            throw new ModbusIOException("I/O exception - failed to write", ex);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,93 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.io;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.EOFException;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Interface implementing a non word data handler for the read/write multiple
 | 
				
			||||||
 | 
					 * register commands.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This interface can be used by any class which works with multiple words of
 | 
				
			||||||
 | 
					 * data for a non-standard data item. For example, message may involve data
 | 
				
			||||||
 | 
					 * items which are floating point values or string.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 * @deprecated In the interests of keeping the library simple, this will be removed in a future release
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Deprecated
 | 
				
			||||||
 | 
					public interface NonWordDataHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the intermediate raw non-word data.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * An implementation would need to provide a means of converting between the
 | 
				
			||||||
 | 
					     * raw byte data and the registers that are present in actual messages.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the raw data as <tt>byte[]</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    byte[] getData();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reads the non-word raw data based on an arbitrary implemented structure.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param in        the <tt>DataInput</tt> to read from.
 | 
				
			||||||
 | 
					     * @param reference to specify the offset as <tt>int</tt>.
 | 
				
			||||||
 | 
					     * @param count     to specify the amount of bytes as <tt>int</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IOException  if I/O fails.
 | 
				
			||||||
 | 
					     * @throws EOFException if the stream ends before all data is read.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void readData(DataInput in, int reference, int count) throws IOException, EOFException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the word count of the data. Note that this should be the length
 | 
				
			||||||
 | 
					     * of the byte array divided by two.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of words the data consists of.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int getWordCount();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Commits the data if it has been read into an intermediate repository.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * This method is called for a message (for example, a
 | 
				
			||||||
 | 
					     * <tt>WriteMultipleRegistersRequest</tt> instance) when finished with
 | 
				
			||||||
 | 
					     * reading, for creating a response.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return -1 if the commit was successful, a Modbus exception code valid
 | 
				
			||||||
 | 
					     * for the read/write multiple registers commands otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int commitUpdate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Prepares the raw data, putting it together from a backing data store.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * This method is called for a message (for example, * <tt>ReadMultipleRegistersRequest</tt>) when finished with reading, for
 | 
				
			||||||
 | 
					     * creating a response.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param reference to specify the offset as <tt>int</tt>.
 | 
				
			||||||
 | 
					     * @param count     to specify the number of bytes as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void prepareData(int reference, int count);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,112 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a<tt>ModbusResponse</tt> that represents an exception.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ExceptionResponse extends ModbusResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // instance attributes
 | 
				
			||||||
 | 
					    private int exceptionCode = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ExceptionResponse</tt> instance with a given
 | 
				
			||||||
 | 
					     * function code and an exception code. The function code will be
 | 
				
			||||||
 | 
					     * automatically increased with the exception offset.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param fc  the function code as <tt>int</tt>.
 | 
				
			||||||
 | 
					     * @param exc the exception code as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ExceptionResponse(int fc, int exc) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // One byte of data.
 | 
				
			||||||
 | 
					        setDataLength(1);
 | 
				
			||||||
 | 
					        setFunctionCode(fc | Modbus.EXCEPTION_OFFSET);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        exceptionCode = exc;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ExceptionResponse</tt> instance with a given
 | 
				
			||||||
 | 
					     * function code. ORs the exception offset automatically.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param fc the function code as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ExceptionResponse(int fc) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // One byte of data.
 | 
				
			||||||
 | 
					        setDataLength(1);
 | 
				
			||||||
 | 
					        setFunctionCode(fc | Modbus.EXCEPTION_OFFSET);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ExceptionResponse</tt> instance with no function
 | 
				
			||||||
 | 
					     * or exception code.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ExceptionResponse() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // One byte of data.
 | 
				
			||||||
 | 
					        setDataLength(1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the Modbus exception code of this <tt>ExceptionResponse</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the exception code as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getExceptionCode() {
 | 
				
			||||||
 | 
					        return exceptionCode;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.writeByte(getExceptionCode());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * readData()
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * read the single byte of data, which is the exception code.
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be read from the socket/port
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        exceptionCode = din.readUnsignedByte();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getMessage()
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * return the exception type, which is the "message" for this response.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return -- byte array containing the 1 byte exception code.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte result[] = new byte[1];
 | 
				
			||||||
 | 
					        result[0] = (byte)getExceptionCode();
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @author Julie
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class IllegalAddressExceptionResponse extends ExceptionResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public IllegalAddressExceptionResponse() {
 | 
				
			||||||
 | 
					        super(0, Modbus.ILLEGAL_ADDRESS_EXCEPTION);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public IllegalAddressExceptionResponse(int function) {
 | 
				
			||||||
 | 
					        super(function, Modbus.ILLEGAL_ADDRESS_EXCEPTION);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the function code
 | 
				
			||||||
 | 
					     * @param fc Function code
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setFunctionCode(int fc) {
 | 
				
			||||||
 | 
					        super.setFunctionCode(fc | Modbus.EXCEPTION_OFFSET);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @author jfhaugh
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class IllegalFunctionExceptionResponse extends ExceptionResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public IllegalFunctionExceptionResponse() {
 | 
				
			||||||
 | 
					        super(0, Modbus.ILLEGAL_FUNCTION_EXCEPTION);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public IllegalFunctionExceptionResponse(int function) {
 | 
				
			||||||
 | 
					        super(function | Modbus.EXCEPTION_OFFSET, Modbus.ILLEGAL_FUNCTION_EXCEPTION);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the function code
 | 
				
			||||||
 | 
					     * @param fc Function code
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setFunctionCode(int fc) {
 | 
				
			||||||
 | 
					        super.setFunctionCode(fc | Modbus.EXCEPTION_OFFSET);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,103 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractModbusListener;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * Class implementing a <tt>ModbusRequest</tt> which is created for illegal or
 | 
				
			||||||
 | 
					 * non implemented function codes.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * This is just a helper class to keep the implementation patterns the same for
 | 
				
			||||||
 | 
					 * all cases.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class IllegalFunctionRequest extends ModbusRequest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>IllegalFunctionRequest</tt> instance for a given
 | 
				
			||||||
 | 
					     * function code.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * <p>Used to implement slave devices when an illegal function code
 | 
				
			||||||
 | 
					     * has been requested.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param function the function code as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public IllegalFunctionRequest(int function) {
 | 
				
			||||||
 | 
					        setFunctionCode(function);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>IllegalFunctionRequest</tt> instance for a given
 | 
				
			||||||
 | 
					     * function code.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * <p>Used to implement slave devices when an illegal function code
 | 
				
			||||||
 | 
					     * has been requested.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param unit Unit ID
 | 
				
			||||||
 | 
					     * @param function the function code as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public IllegalFunctionRequest(int unit, int function) {
 | 
				
			||||||
 | 
					        setUnitID(unit);
 | 
				
			||||||
 | 
					        setFunctionCode(function);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * There is no unit number associated with this exception.
 | 
				
			||||||
 | 
					     * @return Modbus excepion response
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse getResponse() {
 | 
				
			||||||
 | 
					        return updateResponseWithHeader(new IllegalFunctionExceptionResponse(getFunctionCode()), true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse createResponse(AbstractModbusListener listener) {
 | 
				
			||||||
 | 
					        return createExceptionResponse(Modbus.ILLEGAL_FUNCTION_EXCEPTION);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        throw new RuntimeException();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Read all of the data that can be read.  This is an unsupported
 | 
				
			||||||
 | 
					     * function, so it may not be possible to know exactly how much data
 | 
				
			||||||
 | 
					     * needs to be read.
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be read from the socket/port
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        // skip all following bytes
 | 
				
			||||||
 | 
					        int length = getDataLength();
 | 
				
			||||||
 | 
					        for (int i = 0; i < length; i++) {
 | 
				
			||||||
 | 
					            din.readByte();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @author Julie
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class IllegalValueExceptionResponse extends ExceptionResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public IllegalValueExceptionResponse() {
 | 
				
			||||||
 | 
					        super(0, Modbus.ILLEGAL_VALUE_EXCEPTION);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public IllegalValueExceptionResponse(int function) {
 | 
				
			||||||
 | 
					        super(function, Modbus.ILLEGAL_VALUE_EXCEPTION);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the function code
 | 
				
			||||||
 | 
					     * @param fc Function code
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setFunctionCode(int fc) {
 | 
				
			||||||
 | 
					        super.setFunctionCode(fc | Modbus.EXCEPTION_OFFSET);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,195 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractModbusListener;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.IllegalAddressException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.ProcessImage;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.Register;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>Mask Write Register</tt> request.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Julie Haugh (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author jfhaugh (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class MaskWriteRegisterRequest extends ModbusRequest {
 | 
				
			||||||
 | 
					    private int reference;
 | 
				
			||||||
 | 
					    private int andMask;
 | 
				
			||||||
 | 
					    private int orMask;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>Mask Write Register</tt> request.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref     Register
 | 
				
			||||||
 | 
					     * @param andMask AND Mask to use
 | 
				
			||||||
 | 
					     * @param orMask  OR Mask to use
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public MaskWriteRegisterRequest(int ref, int andMask, int orMask) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.MASK_WRITE_REGISTER);
 | 
				
			||||||
 | 
					        setReference(ref);
 | 
				
			||||||
 | 
					        setAndMask(andMask);
 | 
				
			||||||
 | 
					        setOrMask(orMask);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setDataLength(6);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>Mask Write Register</tt> request.
 | 
				
			||||||
 | 
					     * instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public MaskWriteRegisterRequest() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.MASK_WRITE_REGISTER);
 | 
				
			||||||
 | 
					        setDataLength(6);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getReference
 | 
				
			||||||
 | 
					     * @return the reference field
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getReference() {
 | 
				
			||||||
 | 
					        return reference;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setReference -- set the reference field.
 | 
				
			||||||
 | 
					     * @param ref the reference field
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setReference(int ref) {
 | 
				
			||||||
 | 
					        reference = ref;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getAndMask -- return the AND mask value;
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return int
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getAndMask() {
 | 
				
			||||||
 | 
					        return andMask;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setAndMask -- set AND mask
 | 
				
			||||||
 | 
					     * @param mask AND mask
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setAndMask(int mask) {
 | 
				
			||||||
 | 
					        andMask = mask;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getOrMask -- return the OR mask value;
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return int
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getOrMask() {
 | 
				
			||||||
 | 
					        return orMask;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setOrMask -- set OR mask
 | 
				
			||||||
 | 
					     * @param mask OR mask
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setOrMask(int mask) {
 | 
				
			||||||
 | 
					        orMask = mask;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getResponse -- create an empty response for this request.
 | 
				
			||||||
 | 
					     * @return empty response for this request
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse getResponse() {
 | 
				
			||||||
 | 
					        return updateResponseWithHeader(new MaskWriteRegisterResponse());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse createResponse(AbstractModbusListener listener) {
 | 
				
			||||||
 | 
					        MaskWriteRegisterResponse response;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Get the process image.
 | 
				
			||||||
 | 
					        ProcessImage procimg = listener.getProcessImage(getUnitID());
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            Register register = procimg.getRegister(reference);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /*
 | 
				
			||||||
 | 
					             * Get the original value.  The AND mask will first be
 | 
				
			||||||
 | 
					             * applied to clear any bits, then the OR mask will be
 | 
				
			||||||
 | 
					             * applied to set them.
 | 
				
			||||||
 | 
					             */
 | 
				
			||||||
 | 
					            int value = register.getValue();
 | 
				
			||||||
 | 
					            value = (value & andMask) | (orMask & ~andMask);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Store the modified value back where it came from.
 | 
				
			||||||
 | 
					            register.setValue(value);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (IllegalAddressException iaex) {
 | 
				
			||||||
 | 
					            return createExceptionResponse(Modbus.ILLEGAL_ADDRESS_EXCEPTION);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        response = (MaskWriteRegisterResponse)getResponse();
 | 
				
			||||||
 | 
					        response.setReference(reference);
 | 
				
			||||||
 | 
					        response.setAndMask(andMask);
 | 
				
			||||||
 | 
					        response.setOrMask(orMask);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * writeData -- output this Modbus message to dout.
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be written from the socket/port
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.write(getMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * readData -- dummy function.  There is no data with the request.
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be read from the socket/port
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        reference = din.readUnsignedShort();
 | 
				
			||||||
 | 
					        andMask = din.readUnsignedShort();
 | 
				
			||||||
 | 
					        orMask = din.readUnsignedShort();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getMessage -- return an empty array as there is no data for
 | 
				
			||||||
 | 
					     * this request.
 | 
				
			||||||
 | 
					     * @return message payload
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte results[] = new byte[6];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        results[0] = (byte)(reference >> 8);
 | 
				
			||||||
 | 
					        results[1] = (byte)(reference & 0xFF);
 | 
				
			||||||
 | 
					        results[2] = (byte)(andMask >> 8);
 | 
				
			||||||
 | 
					        results[3] = (byte)(andMask & 0xFF);
 | 
				
			||||||
 | 
					        results[4] = (byte)(orMask >> 8);
 | 
				
			||||||
 | 
					        results[5] = (byte)(orMask & 0xFF);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return results;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,136 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>ReadMEIResponse</tt>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Derived from similar class for Read Coils response.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Julie Haugh (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class MaskWriteRegisterResponse
 | 
				
			||||||
 | 
					        extends ModbusResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Message fields.
 | 
				
			||||||
 | 
					    private int reference;
 | 
				
			||||||
 | 
					    private int andMask;
 | 
				
			||||||
 | 
					    private int orMask;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ReportSlaveIDResponse</tt>
 | 
				
			||||||
 | 
					     * instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public MaskWriteRegisterResponse() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.MASK_WRITE_REGISTER);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getReference
 | 
				
			||||||
 | 
					     * @return the reference field
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getReference() {
 | 
				
			||||||
 | 
					        return reference;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setReference -- set the reference field.
 | 
				
			||||||
 | 
					     * @param ref Register value
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setReference(int ref) {
 | 
				
			||||||
 | 
					        reference = ref;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getAndMask -- return the AND mask value;
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return int
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getAndMask() {
 | 
				
			||||||
 | 
					        return andMask;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setAndMask -- set AND mask
 | 
				
			||||||
 | 
					     * @param mask Mask to use
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setAndMask(int mask) {
 | 
				
			||||||
 | 
					        andMask = mask;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getOrMask -- return the OR mask value;
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return int
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getOrMask() {
 | 
				
			||||||
 | 
					        return orMask;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setOrMask -- set OR mask
 | 
				
			||||||
 | 
					     * @param mask OR bit mask
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setOrMask(int mask) {
 | 
				
			||||||
 | 
					        orMask = mask;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * writeData -- output the completed Modbus message to dout
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be written to the socket/port
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.write(getMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * readData -- input the Modbus message from din.  If there was a
 | 
				
			||||||
 | 
					     * header, such as for Modbus/TCP, it will have been read
 | 
				
			||||||
 | 
					     * already.
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be read from the socket/port
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        reference = din.readUnsignedShort();
 | 
				
			||||||
 | 
					        andMask = din.readUnsignedShort();
 | 
				
			||||||
 | 
					        orMask = din.readUnsignedShort();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getMessage -- format the message into a byte array.
 | 
				
			||||||
 | 
					     * @return Byte array of the message
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte results[] = new byte[6];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        results[0] = (byte)(reference >> 8);
 | 
				
			||||||
 | 
					        results[1] = (byte)(reference & 0xFF);
 | 
				
			||||||
 | 
					        results[2] = (byte)(andMask >> 8);
 | 
				
			||||||
 | 
					        results[3] = (byte)(andMask & 0xFF);
 | 
				
			||||||
 | 
					        results[4] = (byte)(orMask >> 8);
 | 
				
			||||||
 | 
					        results[5] = (byte)(orMask & 0xFF);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return results;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,154 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Interface defining a ModbusMessage.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public interface ModbusMessage {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Check the flag which indicates that this <tt>ModbusMessage</tt> is for a
 | 
				
			||||||
 | 
					     * headless (serial, or headless networked) connection.
 | 
				
			||||||
 | 
					     * @return is for a headless (serial, or headless networked) connection
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    boolean isHeadless();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the flag that marks this <tt>ModbusMessage</tt> as headless (for
 | 
				
			||||||
 | 
					     * serial transport).
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void setHeadless();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the transaction identifier of this <tt>ModbusMessage</tt> as
 | 
				
			||||||
 | 
					     * <tt>int</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * The identifier is a 2-byte (short) non negative integer value valid in
 | 
				
			||||||
 | 
					     * the range of 0-65535.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the transaction identifier as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int getTransactionID();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the protocol identifier of this <tt>ModbusMessage</tt> as
 | 
				
			||||||
 | 
					     * <tt>int</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * The identifier is a 2-byte (short) non negative integer value valid in
 | 
				
			||||||
 | 
					     * the range of 0-65535.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the protocol identifier as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int getProtocolID();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the length of the data appended after the protocol header.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the data length as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int getDataLength();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the unit identifier of this <tt>ModbusMessage</tt> as
 | 
				
			||||||
 | 
					     * <tt>int</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * The identifier is a 1-byte non negative integer value valid in the range
 | 
				
			||||||
 | 
					     * of 0-255.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the unit identifier as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int getUnitID();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the function code of this <tt>ModbusMessage</tt> as <tt>int</tt>.<br>
 | 
				
			||||||
 | 
					     * The function code is a 1-byte non negative integer value valid in the
 | 
				
			||||||
 | 
					     * range of 0-127.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * Function codes are ordered in conformance classes their values are
 | 
				
			||||||
 | 
					     * specified in <tt>com.ghgande.j2mod.modbus.Modbus</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the function code as <tt>int</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @see com.ghgande.j2mod.modbus.Modbus
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int getFunctionCode();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the <i>raw</i> message as an array of bytes.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the <i>raw</i> message as <tt>byte[]</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    byte[] getMessage();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the <i>raw</i> message as <tt>String</tt> containing a
 | 
				
			||||||
 | 
					     * hexadecimal series of bytes.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * This method is specially for debugging purposes, allowing the user to log
 | 
				
			||||||
 | 
					     * the communication in a manner used in the specification document.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the <i>raw</i> message as <tt>String</tt> containing a
 | 
				
			||||||
 | 
					     * hexadecimal series of bytes.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String getHexMessage();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the number of bytes that will
 | 
				
			||||||
 | 
					     * be written by {@link #writeTo(DataOutput)}.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of bytes that will be written as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int getOutputLength();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Writes this <tt>Transportable</tt> to the
 | 
				
			||||||
 | 
					     * given <tt>DataOutput</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param dout the <tt>DataOutput</tt> to write to.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IOException if an I/O error occurs.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void writeTo(DataOutput dout) throws IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reads this <tt>Transportable</tt> from the given
 | 
				
			||||||
 | 
					     * <tt>DataInput</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param din the <tt>DataInput</tt> to read from.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IOException if an I/O error occurs or the data
 | 
				
			||||||
 | 
					     *                             is invalid.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void readFrom(DataInput din) throws IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,220 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.util.ModbusUtil;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Abstract class implementing a <tt>ModbusMessage</tt>. This class provides
 | 
				
			||||||
 | 
					 * specialised implementations with the functionality they have in common.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public abstract class ModbusMessageImpl implements ModbusMessage {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // instance attributes
 | 
				
			||||||
 | 
					    private int transactionID = Modbus.DEFAULT_TRANSACTION_ID;
 | 
				
			||||||
 | 
					    private int protocolID = Modbus.DEFAULT_PROTOCOL_ID;
 | 
				
			||||||
 | 
					    private int dataLength;
 | 
				
			||||||
 | 
					    private int unitID = Modbus.DEFAULT_UNIT_ID;
 | 
				
			||||||
 | 
					    private int functionCode;
 | 
				
			||||||
 | 
					    private boolean headless = false; // flag for header-less (serial)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean isHeadless() {
 | 
				
			||||||
 | 
					        return headless;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void setHeadless() {
 | 
				
			||||||
 | 
					        headless = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public int getTransactionID() {
 | 
				
			||||||
 | 
					        return transactionID & 0x0000FFFF;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the transaction identifier of this <tt>ModbusMessage</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * The identifier must be a 2-byte (short) non negative integer value valid
 | 
				
			||||||
 | 
					     * in the range of 0-65535.<br>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param tid the transaction identifier as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setTransactionID(int tid) {
 | 
				
			||||||
 | 
					        transactionID = tid & 0x0000FFFF;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public int getProtocolID() {
 | 
				
			||||||
 | 
					        return protocolID;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the protocol identifier of this <tt>ModbusMessage</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * The identifier should be a 2-byte (short) non negative integer value
 | 
				
			||||||
 | 
					     * valid in the range of 0-65535.<br>
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param pid the protocol identifier as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setProtocolID(int pid) {
 | 
				
			||||||
 | 
					        protocolID = pid;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public int getDataLength() {
 | 
				
			||||||
 | 
					        return dataLength;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the length of the data appended after the protocol header.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * Note that this library, a bit in contrast to the specification, counts
 | 
				
			||||||
 | 
					     * the unit identifier and the function code in the header, because it is
 | 
				
			||||||
 | 
					     * part of each and every message. Thus this method will add two (2) to the
 | 
				
			||||||
 | 
					     * passed in integer value.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * This method does not include the length of a final CRC/LRC for those
 | 
				
			||||||
 | 
					     * protocols which requirement.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param length the data length as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setDataLength(int length) {
 | 
				
			||||||
 | 
					        if (length < 0 || length + 2 > 255) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException("Invalid length: " + length);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dataLength = length + 2;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public int getUnitID() {
 | 
				
			||||||
 | 
					        return unitID;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the unit identifier of this <tt>ModbusMessage</tt>.<br>
 | 
				
			||||||
 | 
					     * The identifier should be a 1-byte non negative integer value valid in the
 | 
				
			||||||
 | 
					     * range of 0-255.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param num the unit identifier number to be set.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setUnitID(int num) {
 | 
				
			||||||
 | 
					        unitID = num;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public int getFunctionCode() {
 | 
				
			||||||
 | 
					        return functionCode;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the function code of this <tt>ModbusMessage</tt>.<br>
 | 
				
			||||||
 | 
					     * The function code should be a 1-byte non negative integer value valid in
 | 
				
			||||||
 | 
					     * the range of 0-127.<br>
 | 
				
			||||||
 | 
					     * Function codes are ordered in conformance classes their values are
 | 
				
			||||||
 | 
					     * specified in <tt>com.ghgande.j2mod.modbus.Modbus</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param code the code of the function to be set.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @see com.ghgande.j2mod.modbus.Modbus
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected void setFunctionCode(int code) {
 | 
				
			||||||
 | 
					        functionCode = code;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public String getHexMessage() {
 | 
				
			||||||
 | 
					        return ModbusUtil.toHex(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the headless flag of this message.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param b true if headless, false otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setHeadless(boolean b) {
 | 
				
			||||||
 | 
					        headless = b;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public int getOutputLength() {
 | 
				
			||||||
 | 
					        int l = 2 + getDataLength();
 | 
				
			||||||
 | 
					        if (!isHeadless()) {
 | 
				
			||||||
 | 
					            l = l + 4;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return l;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void writeTo(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!isHeadless()) {
 | 
				
			||||||
 | 
					            dout.writeShort(getTransactionID());
 | 
				
			||||||
 | 
					            dout.writeShort(getProtocolID());
 | 
				
			||||||
 | 
					            dout.writeShort(getDataLength());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        dout.writeByte(getUnitID());
 | 
				
			||||||
 | 
					        dout.writeByte(getFunctionCode());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        writeData(dout);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void readFrom(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        if (!isHeadless()) {
 | 
				
			||||||
 | 
					            setTransactionID(din.readUnsignedShort());
 | 
				
			||||||
 | 
					            setProtocolID(din.readUnsignedShort());
 | 
				
			||||||
 | 
					            dataLength = din.readUnsignedShort();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        setUnitID(din.readUnsignedByte());
 | 
				
			||||||
 | 
					        setFunctionCode(din.readUnsignedByte());
 | 
				
			||||||
 | 
					        readData(din);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Writes the subclass specific data to the given DataOutput.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param dout the DataOutput to be written to.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IOException if an I/O related error occurs.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract void writeData(DataOutput dout) throws IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reads the subclass specific data from the given DataInput instance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param din the DataInput to read from.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IOException if an I/O related error occurs.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract void readData(DataInput din) throws IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,189 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractModbusListener;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Abstract class implementing a <tt>ModbusRequest</tt>. This class provides
 | 
				
			||||||
 | 
					 * specialised implementations with the functionality they have in common.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author jfhaugh (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public abstract class ModbusRequest extends ModbusMessageImpl {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Factory method creating the required specialized <tt>ModbusRequest</tt>
 | 
				
			||||||
 | 
					     * instance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param functionCode the function code of the request as <tt>int</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a ModbusRequest instance specific for the given function type.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static ModbusRequest createModbusRequest(int functionCode) {
 | 
				
			||||||
 | 
					        ModbusRequest request;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        switch (functionCode) {
 | 
				
			||||||
 | 
					            case Modbus.READ_COILS:
 | 
				
			||||||
 | 
					                request = new ReadCoilsRequest();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.READ_INPUT_DISCRETES:
 | 
				
			||||||
 | 
					                request = new ReadInputDiscretesRequest();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.READ_MULTIPLE_REGISTERS:
 | 
				
			||||||
 | 
					                request = new ReadMultipleRegistersRequest();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.READ_INPUT_REGISTERS:
 | 
				
			||||||
 | 
					                request = new ReadInputRegistersRequest();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.WRITE_COIL:
 | 
				
			||||||
 | 
					                request = new WriteCoilRequest();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.WRITE_SINGLE_REGISTER:
 | 
				
			||||||
 | 
					                request = new WriteSingleRegisterRequest();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.WRITE_MULTIPLE_COILS:
 | 
				
			||||||
 | 
					                request = new WriteMultipleCoilsRequest();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.WRITE_MULTIPLE_REGISTERS:
 | 
				
			||||||
 | 
					                request = new WriteMultipleRegistersRequest();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.READ_EXCEPTION_STATUS:
 | 
				
			||||||
 | 
					                request = new ReadExceptionStatusRequest();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.READ_SERIAL_DIAGNOSTICS:
 | 
				
			||||||
 | 
					                request = new ReadSerialDiagnosticsRequest();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.READ_COMM_EVENT_COUNTER:
 | 
				
			||||||
 | 
					                request = new ReadCommEventCounterRequest();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.READ_COMM_EVENT_LOG:
 | 
				
			||||||
 | 
					                request = new ReadCommEventLogRequest();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.REPORT_SLAVE_ID:
 | 
				
			||||||
 | 
					                request = new ReportSlaveIDRequest();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.READ_FILE_RECORD:
 | 
				
			||||||
 | 
					                request = new ReadFileRecordRequest();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.WRITE_FILE_RECORD:
 | 
				
			||||||
 | 
					                request = new WriteFileRecordRequest();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.MASK_WRITE_REGISTER:
 | 
				
			||||||
 | 
					                request = new MaskWriteRegisterRequest();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.READ_WRITE_MULTIPLE:
 | 
				
			||||||
 | 
					                request = new ReadWriteMultipleRequest();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.READ_FIFO_QUEUE:
 | 
				
			||||||
 | 
					                request = new ReadFIFOQueueRequest();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.READ_MEI:
 | 
				
			||||||
 | 
					                request = new ReadMEIRequest();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                request = new IllegalFunctionRequest(functionCode);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return request;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the <tt>ModbusResponse</tt> that correlates with this
 | 
				
			||||||
 | 
					     * <tt>ModbusRequest</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * The response must include the unit number, function code, as well as any
 | 
				
			||||||
 | 
					     * transport-specific header information.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * This method is used to create an empty response which must be populated
 | 
				
			||||||
 | 
					     * by the caller. It is commonly used to un-marshal responses from Modbus
 | 
				
			||||||
 | 
					     * slaves.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the corresponding <tt>ModbusResponse</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract ModbusResponse getResponse();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the <tt>ModbusResponse</tt> that represents the answer to this
 | 
				
			||||||
 | 
					     * <tt>ModbusRequest</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * The implementation should take care about assembling the reply to this
 | 
				
			||||||
 | 
					     * <tt>ModbusRequest</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * This method is used to create responses from the process image associated
 | 
				
			||||||
 | 
					     * with the listener. It is commonly used to implement Modbus slave
 | 
				
			||||||
 | 
					     * instances.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param listener Listener that received the request
 | 
				
			||||||
 | 
					     * @return the corresponding <tt>ModbusResponse</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract ModbusResponse createResponse(AbstractModbusListener listener);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Factory method for creating exception responses with the given exception
 | 
				
			||||||
 | 
					     * code.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param code the code of the exception.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a ModbusResponse instance representing the exception response.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusResponse createExceptionResponse(int code) {
 | 
				
			||||||
 | 
					        return updateResponseWithHeader(new ExceptionResponse(getFunctionCode(), code), true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Updates the response with the header information to match the request
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param response Response to update
 | 
				
			||||||
 | 
					     * @return Updated response
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ModbusResponse updateResponseWithHeader(ModbusResponse response) {
 | 
				
			||||||
 | 
					        return updateResponseWithHeader(response, false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Updates the response with the header information to match the request
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param response Response to update
 | 
				
			||||||
 | 
					     * @param ignoreFunctionCode True if the function code should stay unmolested
 | 
				
			||||||
 | 
					     * @return Updated response
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ModbusResponse updateResponseWithHeader(ModbusResponse response, boolean ignoreFunctionCode) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // transfer header data
 | 
				
			||||||
 | 
					        response.setHeadless(isHeadless());
 | 
				
			||||||
 | 
					        if (!isHeadless()) {
 | 
				
			||||||
 | 
					            response.setTransactionID(getTransactionID());
 | 
				
			||||||
 | 
					            response.setProtocolID(getProtocolID());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            response.setHeadless();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        response.setUnitID(getUnitID());
 | 
				
			||||||
 | 
					        if (!ignoreFunctionCode) {
 | 
				
			||||||
 | 
					            response.setFunctionCode(getFunctionCode());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,162 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.ByteArrayInputStream;
 | 
				
			||||||
 | 
					import java.io.DataInputStream;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static com.ghgande.j2mod.modbus.msg.ModbusResponse.AuxiliaryMessageTypes.NONE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Abstract class implementing a <tt>ModbusResponse</tt>. This class provides
 | 
				
			||||||
 | 
					 * specialised implementations with the functionality they have in common.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Julie Haugh
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public abstract class ModbusResponse extends ModbusMessageImpl {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final Logger logger = LoggerFactory.getLogger(ModbusResponse.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public enum AuxiliaryMessageTypes {
 | 
				
			||||||
 | 
					        NONE, UNIT_ID_MISSMATCH
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    private AuxiliaryMessageTypes auxiliaryType = NONE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Factory method creating the required specialized <tt>ModbusResponse</tt>
 | 
				
			||||||
 | 
					     * instance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param functionCode the function code of the response as <tt>int</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a ModbusResponse instance specific for the given function code.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static ModbusResponse createModbusResponse(int functionCode) {
 | 
				
			||||||
 | 
					        ModbusResponse response;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        switch (functionCode) {
 | 
				
			||||||
 | 
					            case Modbus.READ_COILS:
 | 
				
			||||||
 | 
					                response = new ReadCoilsResponse();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.READ_INPUT_DISCRETES:
 | 
				
			||||||
 | 
					                response = new ReadInputDiscretesResponse();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.READ_MULTIPLE_REGISTERS:
 | 
				
			||||||
 | 
					                response = new ReadMultipleRegistersResponse();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.READ_INPUT_REGISTERS:
 | 
				
			||||||
 | 
					                response = new ReadInputRegistersResponse();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.WRITE_COIL:
 | 
				
			||||||
 | 
					                response = new WriteCoilResponse();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.WRITE_SINGLE_REGISTER:
 | 
				
			||||||
 | 
					                response = new WriteSingleRegisterResponse();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.WRITE_MULTIPLE_COILS:
 | 
				
			||||||
 | 
					                response = new WriteMultipleCoilsResponse();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.WRITE_MULTIPLE_REGISTERS:
 | 
				
			||||||
 | 
					                response = new WriteMultipleRegistersResponse();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.READ_EXCEPTION_STATUS:
 | 
				
			||||||
 | 
					                response = new ReadExceptionStatusResponse();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.READ_SERIAL_DIAGNOSTICS:
 | 
				
			||||||
 | 
					                response = new ReadSerialDiagnosticsResponse();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.READ_COMM_EVENT_COUNTER:
 | 
				
			||||||
 | 
					                response = new ReadCommEventCounterResponse();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.READ_COMM_EVENT_LOG:
 | 
				
			||||||
 | 
					                response = new ReadCommEventLogResponse();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.REPORT_SLAVE_ID:
 | 
				
			||||||
 | 
					                response = new ReportSlaveIDResponse();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.READ_FILE_RECORD:
 | 
				
			||||||
 | 
					                response = new ReadFileRecordResponse();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.WRITE_FILE_RECORD:
 | 
				
			||||||
 | 
					                response = new WriteFileRecordResponse();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.MASK_WRITE_REGISTER:
 | 
				
			||||||
 | 
					                response = new MaskWriteRegisterResponse();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.READ_WRITE_MULTIPLE:
 | 
				
			||||||
 | 
					                response = new ReadWriteMultipleResponse();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.READ_FIFO_QUEUE:
 | 
				
			||||||
 | 
					                response = new ReadFIFOQueueResponse();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case Modbus.READ_MEI:
 | 
				
			||||||
 | 
					                response = new ReadMEIResponse();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                if ((functionCode & 0x80) != 0) {
 | 
				
			||||||
 | 
					                    response = new ExceptionResponse(functionCode);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else {
 | 
				
			||||||
 | 
					                    response = new ExceptionResponse();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Utility method to set the raw data of the message. Should not be used
 | 
				
			||||||
 | 
					     * except under rare circumstances.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param msg the <tt>byte[]</tt> resembling the raw modbus response
 | 
				
			||||||
 | 
					     *            message.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected void setMessage(byte[] msg) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            readData(new DataInputStream(new ByteArrayInputStream(msg)));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (IOException ex) {
 | 
				
			||||||
 | 
					            logger.error("Problem setting response message - {}", ex.getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the auxiliary type of this response message
 | 
				
			||||||
 | 
					     * Useful for adding extra information to the message that can be used by downstream processing
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Auxiliary type
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public AuxiliaryMessageTypes getAuxiliaryType() {
 | 
				
			||||||
 | 
					        return auxiliaryType;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the auxiliary type of this response
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param auxiliaryType Type
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setAuxiliaryType(AuxiliaryMessageTypes auxiliaryType) {
 | 
				
			||||||
 | 
					        this.auxiliaryType = auxiliaryType;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,176 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractModbusListener;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.DigitalOut;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.IllegalAddressException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.ProcessImage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>ReadCoilsRequest</tt>. The implementation directly
 | 
				
			||||||
 | 
					 * correlates with the class 1 function <i>read coils (FC 1)</i>. It
 | 
				
			||||||
 | 
					 * encapsulates the corresponding request message.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * Coils are understood as bits that can be manipulated (i.e. set or unset).
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author jfhaugh
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReadCoilsRequest extends ModbusRequest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // instance attributes
 | 
				
			||||||
 | 
					    private int reference;
 | 
				
			||||||
 | 
					    private int bitCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ReadCoilsRequest</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadCoilsRequest() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_COILS);
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ReadCoilsRequest</tt> instance with a given
 | 
				
			||||||
 | 
					     * reference and count of coils (i.e. bits) to be read.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref   the reference number of the register to read from.
 | 
				
			||||||
 | 
					     * @param count the number of bits to be read.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadCoilsRequest(int ref, int count) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_COILS);
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setReference(ref);
 | 
				
			||||||
 | 
					        setBitCount(count);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse getResponse() {
 | 
				
			||||||
 | 
					        return updateResponseWithHeader(new ReadCoilsResponse(bitCount));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse createResponse(AbstractModbusListener listener) {
 | 
				
			||||||
 | 
					        ModbusResponse response;
 | 
				
			||||||
 | 
					        DigitalOut[] douts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 1. get process image
 | 
				
			||||||
 | 
					        ProcessImage procimg = listener.getProcessImage(getUnitID());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 2. get input discretes range
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            douts = procimg.getDigitalOutRange(getReference(), getBitCount());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (IllegalAddressException e) {
 | 
				
			||||||
 | 
					            return createExceptionResponse(Modbus.ILLEGAL_ADDRESS_EXCEPTION);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        response = getResponse();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Populate the discrete values from the process image.
 | 
				
			||||||
 | 
					        for (int i = 0; i < douts.length; i++) {
 | 
				
			||||||
 | 
					            ((ReadCoilsResponse)response).setCoilStatus(i, douts[i].isSet());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the reference of the register to to start reading from with this
 | 
				
			||||||
 | 
					     * <tt>ReadCoilsRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the reference of the register to start reading from as
 | 
				
			||||||
 | 
					     * <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getReference() {
 | 
				
			||||||
 | 
					        return reference;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the reference of the register to start reading from with this
 | 
				
			||||||
 | 
					     * <tt>ReadCoilsRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref the reference of the register to start reading from.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setReference(int ref) {
 | 
				
			||||||
 | 
					        reference = ref;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the number of bits (i.e. coils) to be read with this
 | 
				
			||||||
 | 
					     * <tt>ReadCoilsRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of bits to be read.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getBitCount() {
 | 
				
			||||||
 | 
					        return bitCount;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the number of bits (i.e. coils) to be read with this
 | 
				
			||||||
 | 
					     * <tt>ReadCoilsRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param count the number of bits to be read.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setBitCount(int count) {
 | 
				
			||||||
 | 
					        if (count > Modbus.MAX_BITS) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException("Maximum bitcount exceeded");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            bitCount = count;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.write(getMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        reference = din.readUnsignedShort();
 | 
				
			||||||
 | 
					        bitCount = din.readUnsignedShort();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte result[] = new byte[4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result[0] = (byte)((reference >> 8) & 0xff);
 | 
				
			||||||
 | 
					        result[1] = (byte)((reference & 0xff));
 | 
				
			||||||
 | 
					        result[2] = (byte)((bitCount >> 8) & 0xff);
 | 
				
			||||||
 | 
					        result[3] = (byte)((bitCount & 0xff));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,167 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.util.BitVector;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>ReadCoilsResponse</tt>.
 | 
				
			||||||
 | 
					 * The implementation directly correlates with the class 1
 | 
				
			||||||
 | 
					 * function <i>read coils (FC 1)</i>. It encapsulates
 | 
				
			||||||
 | 
					 * the corresponding response message.
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * Coils are understood as bits that can be manipulated
 | 
				
			||||||
 | 
					 * (i.e. set or unset).
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @version 1.2rc1 (09/11/2004)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Completed re-implementation 1/10/2011
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Created getMessage() method to abstractly create the message
 | 
				
			||||||
 | 
					 * data.
 | 
				
			||||||
 | 
					 * Cleaned up the constructors.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReadCoilsResponse extends ModbusResponse {
 | 
				
			||||||
 | 
					    private BitVector coils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * ReadCoilsResponse -- create an empty response message to be
 | 
				
			||||||
 | 
					     * filled in later.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadCoilsResponse() {
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_COILS);
 | 
				
			||||||
 | 
					        setDataLength(1);
 | 
				
			||||||
 | 
					        coils = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * ReadCoilsResponse -- create a response for a given number of
 | 
				
			||||||
 | 
					     * coils.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param count the number of bits to be read.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadCoilsResponse(int count) {
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_COILS);
 | 
				
			||||||
 | 
					        coils = new BitVector(count);
 | 
				
			||||||
 | 
					        setDataLength(coils.byteSize() + 1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getBitCount -- return the number of coils
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return number of defined coils
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getBitCount() {
 | 
				
			||||||
 | 
					        if (coils == null) {
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            return coils.size();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getCoils -- get the coils bit vector.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * The coils vector may be read (when operating as a master) or
 | 
				
			||||||
 | 
					     * written (when operating as a slave).
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return BitVector containing the coils.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public BitVector getCoils() {
 | 
				
			||||||
 | 
					        return coils;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Convenience method that returns the state
 | 
				
			||||||
 | 
					     * of the bit at the given index.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param index the index of the coil for which
 | 
				
			||||||
 | 
					     *              the status should be returned.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return true if set, false otherwise.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IndexOutOfBoundsException if the
 | 
				
			||||||
 | 
					     *                                   index is out of bounds
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean getCoilStatus(int index) throws IndexOutOfBoundsException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (index < 0) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException(index + " < 0");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (index > coils.size()) {
 | 
				
			||||||
 | 
					            throw new IndexOutOfBoundsException(index + " > " + coils.size());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return coils.getBit(index);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the status of the given coil.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param index the index of the coil to be set.
 | 
				
			||||||
 | 
					     * @param b     true if to be set, false for reset.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setCoilStatus(int index, boolean b) {
 | 
				
			||||||
 | 
					        if (index < 0) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException(index + " < 0");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (index > coils.size()) {
 | 
				
			||||||
 | 
					            throw new IndexOutOfBoundsException(index + " > " + coils.size());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        coils.setBit(index, b);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeData(DataOutput output) throws IOException {
 | 
				
			||||||
 | 
					        byte result[] = getMessage();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        output.write(result);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readData(DataInput input) throws IOException {
 | 
				
			||||||
 | 
					        int count = input.readUnsignedByte();
 | 
				
			||||||
 | 
					        byte[] data = new byte[count];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        input.readFully(data, 0, count);
 | 
				
			||||||
 | 
					        coils = BitVector.createBitVector(data);
 | 
				
			||||||
 | 
					        setDataLength(count + 1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        int len = 1 + coils.byteSize();
 | 
				
			||||||
 | 
					        byte result[] = new byte[len];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result[0] = (byte)coils.byteSize();
 | 
				
			||||||
 | 
					        System.arraycopy(coils.getBytes(), 0, result, 1, coils.byteSize());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,81 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractModbusListener;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>Read MEI Data</tt> request.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Julie Haugh (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author jfhaugh (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReadCommEventCounterRequest extends ModbusRequest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>Report Slave ID request</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadCommEventCounterRequest() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_COMM_EVENT_COUNTER);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // There is no additional data in this request.
 | 
				
			||||||
 | 
					        setDataLength(0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse getResponse() {
 | 
				
			||||||
 | 
					        return updateResponseWithHeader(new ReadCommEventCounterResponse());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse createResponse(AbstractModbusListener listener) {
 | 
				
			||||||
 | 
					        return createExceptionResponse(Modbus.ILLEGAL_FUNCTION_EXCEPTION);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * writeData -- output this Modbus message to dout.
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be written
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.write(getMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * readData -- dummy function.  There is no additional data
 | 
				
			||||||
 | 
					     * to read.
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be read
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getMessage
 | 
				
			||||||
 | 
					     * @return an empty array as there is no data for this request
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return new byte[0];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,117 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>ReadCommEventCounterResponse</tt>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Julie Haugh (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReadCommEventCounterResponse extends ModbusResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Message fields.
 | 
				
			||||||
 | 
					    private int status;
 | 
				
			||||||
 | 
					    private int events;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ReportSlaveIDResponse</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadCommEventCounterResponse() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_COMM_EVENT_COUNTER);
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getStatus -- get the device's status.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return int
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getStatus() {
 | 
				
			||||||
 | 
					        return status;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setStatus -- set the device's status.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param status int
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setStatus(int status) {
 | 
				
			||||||
 | 
					        if (status != 0 && status != 0xFFFF) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException("Illegal status value: " + status);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.status = status;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getEvents -- get device's event counter.
 | 
				
			||||||
 | 
					     * @return Event count
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getEventCount() {
 | 
				
			||||||
 | 
					        return events;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setEvents -- set the device's event counter.
 | 
				
			||||||
 | 
					     * @param count Event count
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setEventCount(int count) {
 | 
				
			||||||
 | 
					        events = count;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * writeData -- output the completed Modbus message to dout
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be written
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.write(getMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * readData -- input the Modbus message from din. If there was a header,
 | 
				
			||||||
 | 
					     * such as for Modbus/TCP, it will have been read already.
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be read
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        status = din.readUnsignedShort();
 | 
				
			||||||
 | 
					        events = din.readUnsignedShort();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getMessage -- format the message into a byte array.
 | 
				
			||||||
 | 
					     * @return Response as byte array
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte result[] = new byte[4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result[0] = (byte)(status >> 8);
 | 
				
			||||||
 | 
					        result[1] = (byte)(status & 0xFF);
 | 
				
			||||||
 | 
					        result[2] = (byte)(events >> 8);
 | 
				
			||||||
 | 
					        result[3] = (byte)(events & 0xFF);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,81 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractModbusListener;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>Read MEI Data</tt> request.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Julie Haugh (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author jfhaugh (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReadCommEventLogRequest extends ModbusRequest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>Get Comm Event Log</tt>
 | 
				
			||||||
 | 
					     * instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadCommEventLogRequest() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_COMM_EVENT_LOG);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // There is no additional data in this request.
 | 
				
			||||||
 | 
					        setDataLength(0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse getResponse() {
 | 
				
			||||||
 | 
					        return updateResponseWithHeader(new ReadCommEventLogResponse());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse createResponse(AbstractModbusListener listener) {
 | 
				
			||||||
 | 
					        return createExceptionResponse(Modbus.ILLEGAL_FUNCTION_EXCEPTION);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * writeData -- output this Modbus message to dout.
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be written
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.write(getMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * readData -- dummy function.  There is no data with the request.
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be read
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getMessage
 | 
				
			||||||
 | 
					     * @return an empty array as there is no data for this request
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return new byte[0];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,202 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>ReadCommEventCounterResponse</tt>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Julie Haugh (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReadCommEventLogResponse extends ModbusResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Message fields.
 | 
				
			||||||
 | 
					    private int byteCount;
 | 
				
			||||||
 | 
					    private int status;
 | 
				
			||||||
 | 
					    private int eventCount;
 | 
				
			||||||
 | 
					    private int messageCount;
 | 
				
			||||||
 | 
					    private byte[] events;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ReadCommEventLogResponse</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadCommEventLogResponse() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_COMM_EVENT_LOG);
 | 
				
			||||||
 | 
					        setDataLength(7);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getStatus -- get the device's status.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return int
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getStatus() {
 | 
				
			||||||
 | 
					        return status;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setStatus -- set the device's status.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param status Status to set
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setStatus(int status) {
 | 
				
			||||||
 | 
					        this.status = status;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getEvents -- get device's event counter.
 | 
				
			||||||
 | 
					     * @return Number of events
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getEventCount() {
 | 
				
			||||||
 | 
					        return eventCount;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setEventCount -- set the device's event counter.
 | 
				
			||||||
 | 
					     * @param count Set the event count
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setEventCount(int count) {
 | 
				
			||||||
 | 
					        eventCount = count;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getMessageCount -- get device's message counter.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Number of messages
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getMessageCount() {
 | 
				
			||||||
 | 
					        return messageCount;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setMessageCount -- set device's message counter.
 | 
				
			||||||
 | 
					     * @param count Number of messages
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setMessageCount(int count) {
 | 
				
			||||||
 | 
					        messageCount = count;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getEvent -- get an event from the event log.
 | 
				
			||||||
 | 
					     * @param index Index of the event
 | 
				
			||||||
 | 
					     * @return Event ID
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getEvent(int index) {
 | 
				
			||||||
 | 
					        if (events == null || index < 0 || index >= events.length) {
 | 
				
			||||||
 | 
					            throw new IndexOutOfBoundsException("index = " + index + ", limit = " + (events == null ? "null" : events.length));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return events[index] & 0xFF;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] getEvents() {
 | 
				
			||||||
 | 
					        if (events == null) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        byte[] result = new byte[events.length];
 | 
				
			||||||
 | 
					        System.arraycopy(events, 0, result, 0, events.length);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setEvents(byte[] events) {
 | 
				
			||||||
 | 
					        if (events.length > 64) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException("events list too big (> 64 bytes)");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        events = new byte[events.length];
 | 
				
			||||||
 | 
					        if (events.length > 0) {
 | 
				
			||||||
 | 
					            System.arraycopy(events, 0, events, 0, events.length);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setEvents(int count) {
 | 
				
			||||||
 | 
					        if (count < 0 || count > 64) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException("invalid event list size (0 <= count <= 64)");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        events = new byte[count];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setEvent -- store an event number in the event log
 | 
				
			||||||
 | 
					     * @param index Event position
 | 
				
			||||||
 | 
					     * @param event Event ID
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setEvent(int index, int event) {
 | 
				
			||||||
 | 
					        if (events == null || index < 0 || index >= events.length) {
 | 
				
			||||||
 | 
					            throw new IndexOutOfBoundsException("index = " + index + ", limit = " + (events == null ? "null" : events.length));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        events[index] = (byte)event;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * writeData -- output the completed Modbus message to dout
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be written
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.write(getMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * readData -- input the Modbus message from din. If there was a header,
 | 
				
			||||||
 | 
					     * such as for Modbus/TCP, it will have been read already.
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be read
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        byteCount = din.readByte();
 | 
				
			||||||
 | 
					        status = din.readUnsignedShort();
 | 
				
			||||||
 | 
					        eventCount = din.readUnsignedShort();
 | 
				
			||||||
 | 
					        messageCount = din.readUnsignedShort();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        events = new byte[byteCount - 6];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (events.length > 0) {
 | 
				
			||||||
 | 
					            din.readFully(events, 0, events.length);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getMessage -- format the message into a byte array.
 | 
				
			||||||
 | 
					     * @return Response as byte array
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte result[] = new byte[events.length + 7];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result[0] = (byte)(byteCount = events.length + 6);
 | 
				
			||||||
 | 
					        result[1] = (byte)(status >> 8);
 | 
				
			||||||
 | 
					        result[2] = (byte)(status & 0xFF);
 | 
				
			||||||
 | 
					        result[3] = (byte)(eventCount >> 8);
 | 
				
			||||||
 | 
					        result[4] = (byte)(eventCount & 0xFF);
 | 
				
			||||||
 | 
					        result[5] = (byte)(messageCount >> 8);
 | 
				
			||||||
 | 
					        result[6] = (byte)(messageCount & 0xFF);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        System.arraycopy(events, 0, result, 7, events.length);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,81 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractModbusListener;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>Read Exception Status</tt> request.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Julie Haugh (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author jfhaugh (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReadExceptionStatusRequest extends ModbusRequest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>Read Exception Status</tt> request
 | 
				
			||||||
 | 
					     * instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadExceptionStatusRequest() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_EXCEPTION_STATUS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // There is no additional data in this request.
 | 
				
			||||||
 | 
					        setDataLength(0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse getResponse() {
 | 
				
			||||||
 | 
					        return updateResponseWithHeader(new ReadExceptionStatusResponse());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse createResponse(AbstractModbusListener listener) {
 | 
				
			||||||
 | 
					        return createExceptionResponse(Modbus.ILLEGAL_FUNCTION_EXCEPTION);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * writeData -- output this Modbus message to dout.
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be written
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.write(getMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * readData -- dummy function.  There is no data with the request.
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be read
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getMessage
 | 
				
			||||||
 | 
					     * @return an empty array as there is no data for this request
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return new byte[0];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,92 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>ReadCommEventCounterResponse</tt>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Julie Haugh (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReadExceptionStatusResponse extends ModbusResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Message fields.
 | 
				
			||||||
 | 
					    private int status;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ReadExceptionStatusResponse</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadExceptionStatusResponse() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_EXCEPTION_STATUS);
 | 
				
			||||||
 | 
					        setDataLength(1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getStatus -- get the device's status.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return int
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getStatus() {
 | 
				
			||||||
 | 
					        return status;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setStatus -- set the device's status.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param status Status to set
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setStatus(int status) {
 | 
				
			||||||
 | 
					        this.status = status;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * writeData -- output the completed Modbus message to dout
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be written
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.write(getMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * readData -- input the Modbus message from din. If there was a header,
 | 
				
			||||||
 | 
					     * such as for Modbus/TCP, it will have been read already.
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be read
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        status = din.readByte() & 0xFF;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getMessage -- format the message into a byte array.
 | 
				
			||||||
 | 
					     * @return Response as byte array
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte result[] = new byte[1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result[0] = (byte)(status & 0xFF);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,129 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractModbusListener;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.IllegalAddressException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.InputRegister;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.ProcessImage;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.Register;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>Read FIFO Queue</tt> request.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Julie Haugh (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author jfhaugh (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReadFIFOQueueRequest extends ModbusRequest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private int reference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>Read FIFO Queue</tt> request instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadFIFOQueueRequest() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_FIFO_QUEUE);
 | 
				
			||||||
 | 
					        setDataLength(2);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getReference -- get the queue register number.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return int
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getReference() {
 | 
				
			||||||
 | 
					        return reference;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setReference -- set the queue register number.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref Register
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setReference(int ref) {
 | 
				
			||||||
 | 
					        reference = ref;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse getResponse() {
 | 
				
			||||||
 | 
					        return updateResponseWithHeader(new ReadFIFOQueueResponse());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse createResponse(AbstractModbusListener listener) {
 | 
				
			||||||
 | 
					        ReadFIFOQueueResponse response;
 | 
				
			||||||
 | 
					        InputRegister[] registers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Get the process image.
 | 
				
			||||||
 | 
					        ProcessImage procimg = listener.getProcessImage(getUnitID());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            // Get the FIFO queue location and read the count of available
 | 
				
			||||||
 | 
					            // registers.
 | 
				
			||||||
 | 
					            Register queue = procimg.getRegister(reference);
 | 
				
			||||||
 | 
					            int count = queue.getValue();
 | 
				
			||||||
 | 
					            if (count < 0 || count > 31) {
 | 
				
			||||||
 | 
					                return createExceptionResponse(Modbus.ILLEGAL_VALUE_EXCEPTION);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            registers = procimg.getRegisterRange(reference + 1, count);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (IllegalAddressException e) {
 | 
				
			||||||
 | 
					            return createExceptionResponse(Modbus.ILLEGAL_ADDRESS_EXCEPTION);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        response = (ReadFIFOQueueResponse)getResponse();
 | 
				
			||||||
 | 
					        response.setRegisters(registers);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * writeData -- output this Modbus message to dout.
 | 
				
			||||||
 | 
					     * @throws IOException If cannot write
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.write(getMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * readData -- read the reference word.
 | 
				
			||||||
 | 
					     * @throws IOException If cannot read
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        reference = din.readUnsignedShort();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getMessage
 | 
				
			||||||
 | 
					     * @return an empty array as there is no data for this request
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte results[] = new byte[2];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        results[0] = (byte)(reference >> 8);
 | 
				
			||||||
 | 
					        results[1] = (byte)(reference & 0xFF);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return results;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,161 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.InputRegister;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.SimpleInputRegister;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.util.Arrays;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>ReadFIFOQueueResponse</tt>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Julie Haugh (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReadFIFOQueueResponse extends ModbusResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Message fields.
 | 
				
			||||||
 | 
					    private int count;
 | 
				
			||||||
 | 
					    private InputRegister registers[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ReadFIFOQueueResponse</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadFIFOQueueResponse() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_FIFO_QUEUE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        count = 0;
 | 
				
			||||||
 | 
					        registers = new InputRegister[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setDataLength(7);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getWordCount -- get the queue size.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Word count int
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    synchronized public int getWordCount() {
 | 
				
			||||||
 | 
					        return count;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setWordCount -- set the queue size.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref Register
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized void setWordCount(int ref) {
 | 
				
			||||||
 | 
					        if (ref < 0 || ref > 31) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        count = ref;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    synchronized public int[] getRegisters() {
 | 
				
			||||||
 | 
					        int values[] = new int[count];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (int i = 0; i < count; i++) {
 | 
				
			||||||
 | 
					            values[i] = getRegister(i);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return values;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setRegisters -- set the device's status.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param regs Array of registers
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized void setRegisters(InputRegister[] regs) {
 | 
				
			||||||
 | 
					        if (regs == null) {
 | 
				
			||||||
 | 
					            registers = null;
 | 
				
			||||||
 | 
					            count = 0;
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        registers = Arrays.copyOf(regs, regs.length);
 | 
				
			||||||
 | 
					        if (regs.length > 31) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        count = regs.length;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int getRegister(int index) {
 | 
				
			||||||
 | 
					        return registers[index].getValue();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * writeData -- output the completed Modbus message to dout
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be written
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.write(getMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * readData -- input the Modbus message from din. If there was a header,
 | 
				
			||||||
 | 
					     * such as for Modbus/TCP, it will have been read already.
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be read
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /*
 | 
				
			||||||
 | 
					         * Read and discard the byte count.  There's no way to indicate
 | 
				
			||||||
 | 
					         * the packet was inconsistent, other than throwing an I/O
 | 
				
			||||||
 | 
					         * exception for an invalid packet format ...
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        din.readShort();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // The first register is the number of registers which
 | 
				
			||||||
 | 
					        // follow.  Save that as count, not as a register.
 | 
				
			||||||
 | 
					        count = din.readUnsignedShort();
 | 
				
			||||||
 | 
					        registers = new InputRegister[count];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (int i = 0; i < count; i++) {
 | 
				
			||||||
 | 
					            registers[i] = new SimpleInputRegister(din.readShort());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getMessage -- format the message into a byte array.
 | 
				
			||||||
 | 
					     * @return Byte array of message
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte result[] = new byte[count * 2 + 4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int len = count * 2 + 2;
 | 
				
			||||||
 | 
					        result[0] = (byte)(len >> 8);
 | 
				
			||||||
 | 
					        result[1] = (byte)(len & 0xFF);
 | 
				
			||||||
 | 
					        result[2] = (byte)(count >> 8);
 | 
				
			||||||
 | 
					        result[3] = (byte)(count & 0xFF);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (int i = 0; i < count; i++) {
 | 
				
			||||||
 | 
					            byte value[] = registers[i].toBytes();
 | 
				
			||||||
 | 
					            result[i * 2 + 4] = value[0];
 | 
				
			||||||
 | 
					            result[i * 2 + 5] = value[1];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,264 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ReadFileRecordResponse.RecordResponse;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractModbusListener;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>Read File Record</tt> request.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Julie Haugh (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReadFileRecordRequest extends ModbusRequest {
 | 
				
			||||||
 | 
					    private RecordRequest[] records;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>Read File Record</tt> request instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadFileRecordRequest() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_FILE_RECORD);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Request size byte is all that is required.
 | 
				
			||||||
 | 
					        setDataLength(1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getRequestSize -- return the total request size. This is useful for
 | 
				
			||||||
 | 
					     * determining if a new record can be added.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return size in bytes of response.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getRequestSize() {
 | 
				
			||||||
 | 
					        if (records == null) {
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int size = 1;
 | 
				
			||||||
 | 
					        for (RecordRequest record : records) {
 | 
				
			||||||
 | 
					            size += record.getRequestSize();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return size;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getRequestCount
 | 
				
			||||||
 | 
					     * @return the number of record requests in this message
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getRequestCount() {
 | 
				
			||||||
 | 
					        if (records == null) {
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return records.length;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getRecord
 | 
				
			||||||
 | 
					     * @param index Reference
 | 
				
			||||||
 | 
					     * @return the record request indicated by the reference
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public RecordRequest getRecord(int index) {
 | 
				
			||||||
 | 
					        return records[index];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * addRequest -- add a new record request.
 | 
				
			||||||
 | 
					     * @param request Record request to add
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void addRequest(RecordRequest request) {
 | 
				
			||||||
 | 
					        if (request.getRequestSize() + getRequestSize() > 248) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (records == null) {
 | 
				
			||||||
 | 
					            records = new RecordRequest[1];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            RecordRequest old[] = records;
 | 
				
			||||||
 | 
					            records = new RecordRequest[old.length + 1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            System.arraycopy(old, 0, records, 0, old.length);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        records[records.length - 1] = request;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setDataLength(getRequestSize());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse getResponse() {
 | 
				
			||||||
 | 
					        return updateResponseWithHeader(new ReadFileRecordResponse());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse createResponse(AbstractModbusListener listener) {
 | 
				
			||||||
 | 
					        ReadFileRecordResponse response = (ReadFileRecordResponse)getResponse();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Get the process image.
 | 
				
			||||||
 | 
					        ProcessImage procimg = listener.getProcessImage(getUnitID());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // There is a list of requests to be resolved.
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            for (int i = 0; i < getRequestCount(); i++) {
 | 
				
			||||||
 | 
					                RecordRequest recordRequest = getRecord(i);
 | 
				
			||||||
 | 
					                if (recordRequest.getFileNumber() < 0 || recordRequest.getFileNumber() >= procimg.getFileCount()) {
 | 
				
			||||||
 | 
					                    return createExceptionResponse(Modbus.ILLEGAL_ADDRESS_EXCEPTION);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                File file = procimg.getFileByNumber(recordRequest.getFileNumber());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (recordRequest.getRecordNumber() < 0 || recordRequest.getRecordNumber() >= file.getRecordCount()) {
 | 
				
			||||||
 | 
					                    return createExceptionResponse(Modbus.ILLEGAL_ADDRESS_EXCEPTION);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                Record record = file.getRecord(recordRequest.getRecordNumber());
 | 
				
			||||||
 | 
					                int registers = recordRequest.getWordCount();
 | 
				
			||||||
 | 
					                if (record == null && registers != 0) {
 | 
				
			||||||
 | 
					                    return createExceptionResponse(Modbus.ILLEGAL_ADDRESS_EXCEPTION);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                short data[] = new short[registers];
 | 
				
			||||||
 | 
					                for (int j = 0; j < registers; j++) {
 | 
				
			||||||
 | 
					                    Register register = record.getRegister(j);
 | 
				
			||||||
 | 
					                    if (register == null) {
 | 
				
			||||||
 | 
					                        return createExceptionResponse(Modbus.ILLEGAL_ADDRESS_EXCEPTION);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    data[j] = register.toShort();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                RecordResponse recordResponse = new RecordResponse(data);
 | 
				
			||||||
 | 
					                response.addResponse(recordResponse);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (IllegalAddressException e) {
 | 
				
			||||||
 | 
					            return createExceptionResponse(Modbus.ILLEGAL_ADDRESS_EXCEPTION);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * writeData -- output this Modbus message to dout.
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be written
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.write(getMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * readData -- read all the data for this request.
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be read
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        int byteCount = din.readUnsignedByte();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int recordCount = byteCount / 7;
 | 
				
			||||||
 | 
					        records = new RecordRequest[recordCount];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (int i = 0; i < recordCount; i++) {
 | 
				
			||||||
 | 
					            if (din.readByte() != 6) {
 | 
				
			||||||
 | 
					                throw new IOException();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            int file = din.readUnsignedShort();
 | 
				
			||||||
 | 
					            int record = din.readUnsignedShort();
 | 
				
			||||||
 | 
					            if (record < 0 || record >= 10000) {
 | 
				
			||||||
 | 
					                throw new IOException();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            int count = din.readUnsignedShort();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            records[i] = new RecordRequest(file, record, count);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getMessage
 | 
				
			||||||
 | 
					     * @return the PDU message
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte request[] = new byte[1 + 7 * records.length];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int offset = 0;
 | 
				
			||||||
 | 
					        request[offset++] = (byte)(request.length - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (RecordRequest record : records) {
 | 
				
			||||||
 | 
					            record.getRequest(request, offset);
 | 
				
			||||||
 | 
					            offset += 7;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return request;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static class RecordRequest {
 | 
				
			||||||
 | 
					        private int fileNumber;
 | 
				
			||||||
 | 
					        private int recordNumber;
 | 
				
			||||||
 | 
					        private int wordCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public RecordRequest(int file, int record, int count) {
 | 
				
			||||||
 | 
					            fileNumber = file;
 | 
				
			||||||
 | 
					            recordNumber = record;
 | 
				
			||||||
 | 
					            wordCount = count;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public int getFileNumber() {
 | 
				
			||||||
 | 
					            return fileNumber;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public int getRecordNumber() {
 | 
				
			||||||
 | 
					            return recordNumber;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public int getWordCount() {
 | 
				
			||||||
 | 
					            return wordCount;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * getRequestSize
 | 
				
			||||||
 | 
					         * @return the size of the response in bytes
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        public int getRequestSize() {
 | 
				
			||||||
 | 
					            return 7 + wordCount * 2;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void getRequest(byte[] request, int offset) {
 | 
				
			||||||
 | 
					            request[offset] = 6;
 | 
				
			||||||
 | 
					            request[offset + 1] = (byte)(fileNumber >> 8);
 | 
				
			||||||
 | 
					            request[offset + 2] = (byte)(fileNumber & 0xFF);
 | 
				
			||||||
 | 
					            request[offset + 3] = (byte)(recordNumber >> 8);
 | 
				
			||||||
 | 
					            request[offset + 4] = (byte)(recordNumber & 0xFF);
 | 
				
			||||||
 | 
					            request[offset + 5] = (byte)(wordCount >> 8);
 | 
				
			||||||
 | 
					            request[offset + 6] = (byte)(wordCount & 0xFF);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public byte[] getRequest() {
 | 
				
			||||||
 | 
					            byte[] request = new byte[7];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            getRequest(request, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return request;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,219 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.SimpleRegister;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>ReadFileRecordResponse</tt>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Julie (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReadFileRecordResponse extends ModbusResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private RecordResponse[] records = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ReadFileRecordResponse</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadFileRecordResponse() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_FILE_RECORD);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the number of bytes needed for the response.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * The response is 1 byte for the total response size, plus
 | 
				
			||||||
 | 
					     * the sum of the sizes of all the records in the response.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of bytes in the response.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getByteCount() {
 | 
				
			||||||
 | 
					        if (records == null) {
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int size = 1;
 | 
				
			||||||
 | 
					        for (RecordResponse record : records) {
 | 
				
			||||||
 | 
					            size += record.getResponseSize();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return size;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getRecordCount -- return the number of records in the response.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return count of records in response.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getRecordCount() {
 | 
				
			||||||
 | 
					        if (records == null) {
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return records.length;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getRecord
 | 
				
			||||||
 | 
					     * @param index Record to get
 | 
				
			||||||
 | 
					     * @return the record response indicated by the reference
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public RecordResponse getRecord(int index) {
 | 
				
			||||||
 | 
					        return records[index];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * addResponse -- add a new record response.
 | 
				
			||||||
 | 
					     * @param response Record response to add
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void addResponse(RecordResponse response) {
 | 
				
			||||||
 | 
					        if (records == null) {
 | 
				
			||||||
 | 
					            records = new RecordResponse[1];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            RecordResponse old[] = records;
 | 
				
			||||||
 | 
					            records = new RecordResponse[old.length + 1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            System.arraycopy(old, 0, records, 0, old.length);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        records[records.length - 1] = response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.writeByte(getByteCount() - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (records == null) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (RecordResponse record : records) {
 | 
				
			||||||
 | 
					            dout.write(record.getResponse());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        int byteCount = (din.readUnsignedByte() & 0xFF);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int remainder = byteCount;
 | 
				
			||||||
 | 
					        while (remainder > 0) {
 | 
				
			||||||
 | 
					            int length = din.readUnsignedByte();
 | 
				
			||||||
 | 
					            remainder--;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            int function = din.readByte();
 | 
				
			||||||
 | 
					            remainder--;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (function != 6 || (length - 1) > remainder) {
 | 
				
			||||||
 | 
					                throw new IOException("Invalid response format");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            short[] data = new short[(length - 1) / 2];
 | 
				
			||||||
 | 
					            for (int i = 0; i < data.length; i++) {
 | 
				
			||||||
 | 
					                data[i] = din.readShort();
 | 
				
			||||||
 | 
					                remainder -= 2;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            RecordResponse response = new RecordResponse(data);
 | 
				
			||||||
 | 
					            addResponse(response);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        setDataLength(byteCount + 1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte result[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result = new byte[getByteCount()];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int offset = 0;
 | 
				
			||||||
 | 
					        result[offset++] = (byte)(result.length - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (RecordResponse record : records) {
 | 
				
			||||||
 | 
					            record.getResponse(result, offset);
 | 
				
			||||||
 | 
					            offset += record.getWordCount() * 2;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static class RecordResponse {
 | 
				
			||||||
 | 
					        private int wordCount;
 | 
				
			||||||
 | 
					        private byte[] data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public RecordResponse(short data[]) {
 | 
				
			||||||
 | 
					            wordCount = data.length;
 | 
				
			||||||
 | 
					            this.data = new byte[wordCount * 2];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            int offset = 0;
 | 
				
			||||||
 | 
					            for (int i = 0; i < wordCount; i++) {
 | 
				
			||||||
 | 
					                this.data[offset++] = (byte)(data[i] >> 8);
 | 
				
			||||||
 | 
					                this.data[offset++] = (byte)(data[i] & 0xFF);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public int getWordCount() {
 | 
				
			||||||
 | 
					            return wordCount;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public SimpleRegister getRegister(int register) {
 | 
				
			||||||
 | 
					            if (register < 0 || register >= wordCount) {
 | 
				
			||||||
 | 
					                throw new IndexOutOfBoundsException("0 <= " + register + " < " + wordCount);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            byte b1 = data[register * 2];
 | 
				
			||||||
 | 
					            byte b2 = data[register * 2 + 1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return new SimpleRegister(b1, b2);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * getResponseSize -- return the size of the response in bytes.
 | 
				
			||||||
 | 
					         *
 | 
				
			||||||
 | 
					         * The response is a byte count, a function code, then wordCount
 | 
				
			||||||
 | 
					         * words (2 bytes).
 | 
				
			||||||
 | 
					         * @return the size of the response in bytes
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        public int getResponseSize() {
 | 
				
			||||||
 | 
					            return 2 + (wordCount * 2);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * getResponse - return the response data for this record
 | 
				
			||||||
 | 
					         *
 | 
				
			||||||
 | 
					         * The response data is the byte size of the response, minus this
 | 
				
			||||||
 | 
					         * byte, the function code (6), then the raw byte data for the
 | 
				
			||||||
 | 
					         * registers (wordCount * 2 bytes).
 | 
				
			||||||
 | 
					         *
 | 
				
			||||||
 | 
					         * @param request Request message
 | 
				
			||||||
 | 
					         * @param offset  Offset into buffer
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        public void getResponse(byte[] request, int offset) {
 | 
				
			||||||
 | 
					            request[offset] = (byte)(1 + (wordCount * 2));
 | 
				
			||||||
 | 
					            request[offset + 1] = 6;
 | 
				
			||||||
 | 
					            System.arraycopy(data, 0, request, offset + 2, data.length);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public byte[] getResponse() {
 | 
				
			||||||
 | 
					            byte[] request = new byte[getResponseSize()];
 | 
				
			||||||
 | 
					            getResponse(request, 0);
 | 
				
			||||||
 | 
					            return request;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,177 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractModbusListener;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.DigitalIn;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.IllegalAddressException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.ProcessImage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>ReadInputDiscretesRequest</tt>. The implementation
 | 
				
			||||||
 | 
					 * directly correlates with the class 1 function <i>read input discretes (FC
 | 
				
			||||||
 | 
					 * 2)</i>. It encapsulates the corresponding request message.
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * Input Discretes are understood as bits that cannot be manipulated (i.e. set
 | 
				
			||||||
 | 
					 * or unset).
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author jfhaugh
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReadInputDiscretesRequest extends ModbusRequest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // instance attributes
 | 
				
			||||||
 | 
					    private int reference;
 | 
				
			||||||
 | 
					    private int bitCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ReadInputDiscretesRequest</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadInputDiscretesRequest() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_INPUT_DISCRETES);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Two bytes for count, two bytes for offset.
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ReadInputDiscretesRequest</tt> instance with a given
 | 
				
			||||||
 | 
					     * reference and count of input discretes (i.e. bits) to be read.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref   the reference number of the register to read from.
 | 
				
			||||||
 | 
					     * @param count the number of bits to be read.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadInputDiscretesRequest(int ref, int count) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_INPUT_DISCRETES);
 | 
				
			||||||
 | 
					        // 4 bytes (unit id and function code is excluded)
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					        setReference(ref);
 | 
				
			||||||
 | 
					        setBitCount(count);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse getResponse() {
 | 
				
			||||||
 | 
					        return updateResponseWithHeader(new ReadInputDiscretesResponse(getBitCount()));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse createResponse(AbstractModbusListener listener) {
 | 
				
			||||||
 | 
					        ReadInputDiscretesResponse response;
 | 
				
			||||||
 | 
					        DigitalIn[] dins;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 1. get process image
 | 
				
			||||||
 | 
					        ProcessImage procimg = listener.getProcessImage(getUnitID());
 | 
				
			||||||
 | 
					        // 2. get input discretes range
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            dins = procimg.getDigitalInRange(getReference(), getBitCount());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (IllegalAddressException e) {
 | 
				
			||||||
 | 
					            return createExceptionResponse(Modbus.ILLEGAL_ADDRESS_EXCEPTION);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        response = (ReadInputDiscretesResponse)getResponse();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Populate the discrete values from the process image.
 | 
				
			||||||
 | 
					        for (int i = 0; i < dins.length; i++) {
 | 
				
			||||||
 | 
					            response.setDiscreteStatus(i, dins[i].isSet());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the reference of the discrete to to start reading from with
 | 
				
			||||||
 | 
					     * this <tt>ReadInputDiscretesRequest</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the reference of the discrete to start reading from as
 | 
				
			||||||
 | 
					     * <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getReference() {
 | 
				
			||||||
 | 
					        return reference;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the reference of the register to start reading from with this
 | 
				
			||||||
 | 
					     * <tt>ReadInputDiscretesRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref the reference of the register to start reading from.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setReference(int ref) {
 | 
				
			||||||
 | 
					        if (ref < 0 || bitCount + ref >= 65536) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        reference = ref;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the number of bits (i.e. input discretes) to be read with this
 | 
				
			||||||
 | 
					     * <tt>ReadInputDiscretesRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of bits to be read.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getBitCount() {
 | 
				
			||||||
 | 
					        return bitCount;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the number of bits (i.e. input discretes) to be read with this
 | 
				
			||||||
 | 
					     * <tt>ReadInputDiscretesRequest</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param count the number of bits to be read.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setBitCount(int count) {
 | 
				
			||||||
 | 
					        if (count < 0 || count > 2000 || count + reference >= 65536) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bitCount = count;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.writeShort(reference);
 | 
				
			||||||
 | 
					        dout.writeShort(bitCount);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        reference = din.readUnsignedShort();
 | 
				
			||||||
 | 
					        bitCount = din.readUnsignedShort();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte result[] = new byte[4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result[0] = (byte)((reference >> 8) & 0xff);
 | 
				
			||||||
 | 
					        result[1] = (byte)((reference & 0xff));
 | 
				
			||||||
 | 
					        result[2] = (byte)((bitCount >> 8) & 0xff);
 | 
				
			||||||
 | 
					        result[3] = (byte)((bitCount & 0xff));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,165 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.util.BitVector;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>ReadInputDiscretesResponse</tt>.
 | 
				
			||||||
 | 
					 * The implementation directly correlates with the class 1
 | 
				
			||||||
 | 
					 * function <i>read input discretes (FC 2)</i>. It encapsulates
 | 
				
			||||||
 | 
					 * the corresponding response message.
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * Input Discretes are understood as bits that cannot be
 | 
				
			||||||
 | 
					 * manipulated (i.e. set or unset).
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReadInputDiscretesResponse
 | 
				
			||||||
 | 
					        extends ModbusResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //instance attributes
 | 
				
			||||||
 | 
					    private int bitCount;
 | 
				
			||||||
 | 
					    private BitVector discretes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ReadInputDiscretesResponse</tt>
 | 
				
			||||||
 | 
					     * instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadInputDiscretesResponse() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_INPUT_DISCRETES);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ReadInputDiscretesResponse</tt>
 | 
				
			||||||
 | 
					     * instance with a given count of input discretes
 | 
				
			||||||
 | 
					     * (i.e. bits).
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param count the number of bits to be read.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadInputDiscretesResponse(int count) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_INPUT_DISCRETES);
 | 
				
			||||||
 | 
					        setBitCount(count);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the number of bits (i.e. input discretes)
 | 
				
			||||||
 | 
					     * read with the request.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of bits that have been read.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getBitCount() {
 | 
				
			||||||
 | 
					        return bitCount;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the number of bits in this response.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param count the number of response bits as int.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setBitCount(int count) {
 | 
				
			||||||
 | 
					        bitCount = count;
 | 
				
			||||||
 | 
					        discretes = new BitVector(count);
 | 
				
			||||||
 | 
					        //set correct length, without counting unitid and fc
 | 
				
			||||||
 | 
					        setDataLength(discretes.byteSize() + 1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the <tt>BitVector</tt> that stores
 | 
				
			||||||
 | 
					     * the collection of bits that have been read.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the <tt>BitVector</tt> holding the
 | 
				
			||||||
 | 
					     * bits that have been read.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public BitVector getDiscretes() {
 | 
				
			||||||
 | 
					        return discretes;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Convenience method that returns the state
 | 
				
			||||||
 | 
					     * of the bit at the given index.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param index the index of the input discrete
 | 
				
			||||||
 | 
					     *              for which the status should be returned.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return true if set, false otherwise.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IndexOutOfBoundsException if the
 | 
				
			||||||
 | 
					     *                                   index is out of bounds
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean getDiscreteStatus(int index) throws IndexOutOfBoundsException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return discretes.getBit(index);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the status of the given input discrete.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param index the index of the input discrete to be set.
 | 
				
			||||||
 | 
					     * @param b     true if to be set, false if to be reset.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IndexOutOfBoundsException if the given index exceeds bounds.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setDiscreteStatus(int index, boolean b) throws IndexOutOfBoundsException {
 | 
				
			||||||
 | 
					        discretes.setBit(index, b);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.writeByte(discretes.byteSize());
 | 
				
			||||||
 | 
					        dout.write(discretes.getBytes(), 0, discretes.byteSize());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int count = din.readUnsignedByte();
 | 
				
			||||||
 | 
					        byte[] data = new byte[count];
 | 
				
			||||||
 | 
					        for (int k = 0; k < count; k++) {
 | 
				
			||||||
 | 
					            data[k] = din.readByte();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //decode bytes into bitvector
 | 
				
			||||||
 | 
					        discretes = BitVector.createBitVector(data);
 | 
				
			||||||
 | 
					        if (discretes != null) {
 | 
				
			||||||
 | 
					            bitCount = discretes.size();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //update data length
 | 
				
			||||||
 | 
					        setDataLength(count + 1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte result[];
 | 
				
			||||||
 | 
					        int len = 1 + discretes.byteSize();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result = new byte[len];
 | 
				
			||||||
 | 
					        result[0] = (byte)discretes.byteSize();
 | 
				
			||||||
 | 
					        System.arraycopy(discretes.getBytes(), 0, result, 1, discretes.byteSize());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,163 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractModbusListener;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.IllegalAddressException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.InputRegister;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.ProcessImage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>ReadInputRegistersRequest</tt>. The implementation
 | 
				
			||||||
 | 
					 * directly correlates with the class 0 function <i>read multiple registers (FC
 | 
				
			||||||
 | 
					 * 4)</i>. It encapsulates the corresponding request message.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReadInputRegistersRequest extends ModbusRequest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // instance attributes
 | 
				
			||||||
 | 
					    private int reference;
 | 
				
			||||||
 | 
					    private int wordCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ReadInputRegistersRequest</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadInputRegistersRequest() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_INPUT_REGISTERS);
 | 
				
			||||||
 | 
					        // 4 bytes (unit id and function code is excluded)
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ReadInputRegistersRequest</tt> instance with a given
 | 
				
			||||||
 | 
					     * reference and count of words to be read.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref   the reference number of the register to read from.
 | 
				
			||||||
 | 
					     * @param count the number of words to be read.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadInputRegistersRequest(int ref, int count) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_INPUT_REGISTERS);
 | 
				
			||||||
 | 
					        // 4 bytes (unit id and function code is excluded)
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setReference(ref);
 | 
				
			||||||
 | 
					        setWordCount(count);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ReadInputRegistersResponse getResponse() {
 | 
				
			||||||
 | 
					        ReadInputRegistersResponse response = (ReadInputRegistersResponse)updateResponseWithHeader(new ReadInputRegistersResponse());
 | 
				
			||||||
 | 
					        response.setWordCount(getWordCount());
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse createResponse(AbstractModbusListener listener) {
 | 
				
			||||||
 | 
					        ReadInputRegistersResponse response;
 | 
				
			||||||
 | 
					        InputRegister[] inpregs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 1. get process image
 | 
				
			||||||
 | 
					        ProcessImage procimg = listener.getProcessImage(getUnitID());
 | 
				
			||||||
 | 
					        // 2. get input registers range
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            inpregs = procimg.getInputRegisterRange(getReference(), getWordCount());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (IllegalAddressException iaex) {
 | 
				
			||||||
 | 
					            return createExceptionResponse(Modbus.ILLEGAL_ADDRESS_EXCEPTION);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        response = getResponse();
 | 
				
			||||||
 | 
					        response.setRegisters(inpregs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the reference of the register to to start reading from with this
 | 
				
			||||||
 | 
					     * <tt>ReadInputRegistersRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the reference of the register to start reading from as
 | 
				
			||||||
 | 
					     * <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getReference() {
 | 
				
			||||||
 | 
					        return reference;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the reference of the register to start reading from with this
 | 
				
			||||||
 | 
					     * <tt>ReadInputRegistersRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref the reference of the register to start reading from.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setReference(int ref) {
 | 
				
			||||||
 | 
					        reference = ref;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the number of words to be read with this
 | 
				
			||||||
 | 
					     * <tt>ReadInputRegistersRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of words to be read as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getWordCount() {
 | 
				
			||||||
 | 
					        return wordCount;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the number of words to be read with this
 | 
				
			||||||
 | 
					     * <tt>ReadInputRegistersRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param count the number of words to be read.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setWordCount(int count) {
 | 
				
			||||||
 | 
					        wordCount = count;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.writeShort(reference);
 | 
				
			||||||
 | 
					        dout.writeShort(wordCount);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        reference = din.readUnsignedShort();
 | 
				
			||||||
 | 
					        wordCount = din.readUnsignedShort();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte result[] = new byte[4];
 | 
				
			||||||
 | 
					        result[0] = (byte)((reference >> 8) & 0xff);
 | 
				
			||||||
 | 
					        result[1] = (byte)(reference & 0xff);
 | 
				
			||||||
 | 
					        result[2] = (byte)((wordCount >> 8) & 0xff);
 | 
				
			||||||
 | 
					        result[3] = (byte)(wordCount & 0xff);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,183 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.InputRegister;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.SimpleInputRegister;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.util.Arrays;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>ReadInputRegistersRequest</tt>. The implementation
 | 
				
			||||||
 | 
					 * directly correlates with the class 0 function <i>read multiple registers (FC
 | 
				
			||||||
 | 
					 * 4)</i>. It encapsulates the corresponding response message.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReadInputRegistersResponse extends ModbusResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // instance attributes
 | 
				
			||||||
 | 
					    private int byteCount;
 | 
				
			||||||
 | 
					    private InputRegister[] registers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ReadInputRegistersResponse</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadInputRegistersResponse() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_INPUT_REGISTERS);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ReadInputRegistersResponse</tt> instance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param registers the InputRegister[] holding response input registers.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadInputRegistersResponse(InputRegister[] registers) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_INPUT_REGISTERS);
 | 
				
			||||||
 | 
					        setDataLength(registers == null ? 0 : (registers.length * 2 + 1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.registers = registers == null ? null : Arrays.copyOf(registers, registers.length);
 | 
				
			||||||
 | 
					        byteCount = registers == null ? 0 : (registers.length * 2);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the number of bytes that have been read.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of bytes that have been read as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getByteCount() {
 | 
				
			||||||
 | 
					        return byteCount;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the number of words that have been read. The returned value
 | 
				
			||||||
 | 
					     * should be half as much as the byte count of the response.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of words that have been read as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getWordCount() {
 | 
				
			||||||
 | 
					        return byteCount / 2;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Set the number of words to be written.
 | 
				
			||||||
 | 
					     * @param count Number of words in response
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setWordCount(int count) {
 | 
				
			||||||
 | 
					        byteCount = count * 2;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the <tt>InputRegister</tt> at the given position (relative to the
 | 
				
			||||||
 | 
					     * reference used in the request).
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param index the relative index of the <tt>InputRegister</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the register as <tt>InputRegister</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IndexOutOfBoundsException if the index is out of bounds.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public InputRegister getRegister(int index) throws IndexOutOfBoundsException {
 | 
				
			||||||
 | 
					        if (index < 0) {
 | 
				
			||||||
 | 
					            throw new IndexOutOfBoundsException(index + " < 0");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (index >= getWordCount()) {
 | 
				
			||||||
 | 
					            throw new IndexOutOfBoundsException(index + " >= " + getWordCount());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return registers[index];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the value of the register at the given position (relative to the
 | 
				
			||||||
 | 
					     * reference used in the request) interpreted as usigned short.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param index the relative index of the register for which the value should
 | 
				
			||||||
 | 
					     *              be retrieved.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the unsigned short value as an <tt>int</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IndexOutOfBoundsException if the index is out of bounds.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getRegisterValue(int index) throws IndexOutOfBoundsException {
 | 
				
			||||||
 | 
					        return getRegister(index).toUnsignedShort();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns a reference to the array of input registers read.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a <tt>InputRegister[]</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized InputRegister[] getRegisters() {
 | 
				
			||||||
 | 
					        InputRegister[] dest = new InputRegister[registers.length];
 | 
				
			||||||
 | 
					        System.arraycopy(registers, 0, dest, 0, dest.length);
 | 
				
			||||||
 | 
					        return dest;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the entire block of registers for this response
 | 
				
			||||||
 | 
					     * @param registers Array of registers
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setRegisters(InputRegister[] registers) {
 | 
				
			||||||
 | 
					        setDataLength(registers == null ? 0 : (registers.length * 2 + 1));
 | 
				
			||||||
 | 
					        this.registers = registers == null ? null : Arrays.copyOf(registers, registers.length);
 | 
				
			||||||
 | 
					        byteCount = registers == null ? 0 : (registers.length * 2);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.writeByte(byteCount);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (int k = 0; k < getWordCount(); k++) {
 | 
				
			||||||
 | 
					            dout.write(registers[k].toBytes());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        byteCount = din.readUnsignedByte();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        InputRegister[] registers = new InputRegister[getWordCount()];
 | 
				
			||||||
 | 
					        for (int k = 0; k < getWordCount(); k++) {
 | 
				
			||||||
 | 
					            registers[k] = new SimpleInputRegister(din.readByte(), din.readByte());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this.registers = registers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setDataLength(byteCount);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte result[] = new byte[registers.length * 2 + 1];
 | 
				
			||||||
 | 
					        result[0] = (byte)(registers.length * 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (int i = 0; i < registers.length; i++) {
 | 
				
			||||||
 | 
					            byte value[] = registers[i].toBytes();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            result[1 + i * 2] = value[0];
 | 
				
			||||||
 | 
					            result[2 + i * 2] = value[1];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,178 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractModbusListener;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.EOFException;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>Read MEI Data</tt> request.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author jfhaugh (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReadMEIRequest extends ModbusRequest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // instance attributes
 | 
				
			||||||
 | 
					    private int subCode;
 | 
				
			||||||
 | 
					    private int fieldLevel;
 | 
				
			||||||
 | 
					    private int fieldId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>Read MEI Data request</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadMEIRequest() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_MEI);
 | 
				
			||||||
 | 
					        subCode = 0x0E;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 3 bytes (unit id and function code is excluded)
 | 
				
			||||||
 | 
					        setDataLength(3);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>Read MEI Data request</tt> instance with a given
 | 
				
			||||||
 | 
					     * reference and count of coils (i.e. bits) to be read.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param level the reference number of the register to read from.
 | 
				
			||||||
 | 
					     * @param id    the number of bits to be read.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadMEIRequest(int level, int id) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_MEI);
 | 
				
			||||||
 | 
					        subCode = 0x0E;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 3 bytes (unit id and function code is excluded)
 | 
				
			||||||
 | 
					        setDataLength(3);
 | 
				
			||||||
 | 
					        setLevel(level);
 | 
				
			||||||
 | 
					        setFieldId(id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse getResponse() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Any other sub-function is an error.
 | 
				
			||||||
 | 
					        if (getSubCode() != 0x0E) {
 | 
				
			||||||
 | 
					            IllegalFunctionExceptionResponse error = new IllegalFunctionExceptionResponse();
 | 
				
			||||||
 | 
					            return updateResponseWithHeader(error);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return updateResponseWithHeader(new ReadMEIResponse());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse createResponse(AbstractModbusListener listener) {
 | 
				
			||||||
 | 
					        return createExceptionResponse(Modbus.ILLEGAL_FUNCTION_EXCEPTION);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Gets the MEI subcode associated with this request.
 | 
				
			||||||
 | 
					     * @return The MEI sub code
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getSubCode() {
 | 
				
			||||||
 | 
					        return subCode;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the reference of the register to to start reading from with this
 | 
				
			||||||
 | 
					     * <tt>ReadCoilsRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the reference of the register to start reading from as
 | 
				
			||||||
 | 
					     * <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getLevel() {
 | 
				
			||||||
 | 
					        return fieldLevel;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the reference of the register to start reading from with this
 | 
				
			||||||
 | 
					     * <tt>ReadCoilsRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param level the reference of the register to start reading from.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setLevel(int level) {
 | 
				
			||||||
 | 
					        fieldLevel = level;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the number of bits (i.e. coils) to be read with this
 | 
				
			||||||
 | 
					     * <tt>ReadCoilsRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of bits to be read.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getFieldId() {
 | 
				
			||||||
 | 
					        return fieldId;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the number of bits (i.e. coils) to be read with this
 | 
				
			||||||
 | 
					     * <tt>ReadCoilsRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param id the number of bits to be read.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setFieldId(int id) {
 | 
				
			||||||
 | 
					        fieldId = id;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        byte results[] = new byte[3];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        results[0] = (byte)subCode;
 | 
				
			||||||
 | 
					        results[1] = (byte)fieldLevel;
 | 
				
			||||||
 | 
					        results[2] = (byte)fieldId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dout.write(results);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        subCode = din.readUnsignedByte();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (subCode != 0xE) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                while (din.readByte() >= 0) {
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (EOFException x) {
 | 
				
			||||||
 | 
					                // do nothing.
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        fieldLevel = din.readUnsignedByte();
 | 
				
			||||||
 | 
					        fieldId = din.readUnsignedByte();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte results[] = new byte[3];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        results[0] = (byte)subCode;
 | 
				
			||||||
 | 
					        results[1] = (byte)fieldLevel;
 | 
				
			||||||
 | 
					        results[2] = (byte)fieldId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return results;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,205 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>ReadMEIResponse</tt>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Derived from similar class for Read Coils response.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Julie Haugh (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReadMEIResponse extends ModbusResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final Logger logger = LoggerFactory.getLogger(ReadMEIResponse.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //instance attributes
 | 
				
			||||||
 | 
					    private int fieldLevel = 0;
 | 
				
			||||||
 | 
					    private int conformity = 1;
 | 
				
			||||||
 | 
					    private int fieldCount = 0;
 | 
				
			||||||
 | 
					    private String fields[] = new String[64];
 | 
				
			||||||
 | 
					    private int fieldIds[] = new int[64];
 | 
				
			||||||
 | 
					    private boolean moreFollows = false;
 | 
				
			||||||
 | 
					    private int nextFieldId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ReadMEIResponse</tt>
 | 
				
			||||||
 | 
					     * instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadMEIResponse() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_MEI);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the number of fields
 | 
				
			||||||
 | 
					     * read with the request.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of fields that have been read.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getFieldCount() {
 | 
				
			||||||
 | 
					        if (fields == null) {
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            return fields.length;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the array of strings that were read
 | 
				
			||||||
 | 
					     * @return Array of the fields read
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized String[] getFields() {
 | 
				
			||||||
 | 
					        String[] dest = new String[fields.length];
 | 
				
			||||||
 | 
					        System.arraycopy(fields, 0, dest, 0, dest.length);
 | 
				
			||||||
 | 
					        return dest;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Convenience method that returns the field
 | 
				
			||||||
 | 
					     * at the requested index
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param index the index of the field which
 | 
				
			||||||
 | 
					     *              should be returned.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return requested field
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IndexOutOfBoundsException if the
 | 
				
			||||||
 | 
					     *                                   index is out of bounds
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public String getField(int index) throws IndexOutOfBoundsException {
 | 
				
			||||||
 | 
					        return fields[index];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Convenience method that returns the field
 | 
				
			||||||
 | 
					     * ID at the given index.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param index the index of the field for which
 | 
				
			||||||
 | 
					     *              the ID should be returned.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return field ID
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IndexOutOfBoundsException if the
 | 
				
			||||||
 | 
					     *                                   index is out of bounds
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getFieldId(int index) throws IndexOutOfBoundsException {
 | 
				
			||||||
 | 
					        return fieldIds[index];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setFieldLevel(int level) {
 | 
				
			||||||
 | 
					        fieldLevel = level;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void addField(int id, String text) {
 | 
				
			||||||
 | 
					        fieldIds[fieldCount] = id;
 | 
				
			||||||
 | 
					        fields[fieldCount] = text;
 | 
				
			||||||
 | 
					        fieldCount++;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.write(getMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        int byteCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int subCode = din.readUnsignedByte();
 | 
				
			||||||
 | 
					        if (subCode != 0xE) {
 | 
				
			||||||
 | 
					            throw new IOException("Invalid sub code");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fieldLevel = din.readUnsignedByte();
 | 
				
			||||||
 | 
					        conformity = din.readUnsignedByte();
 | 
				
			||||||
 | 
					        moreFollows = din.readUnsignedByte() == 0xFF;
 | 
				
			||||||
 | 
					        nextFieldId = din.readUnsignedByte();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fieldCount = din.readUnsignedByte();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        byteCount = 6;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (fieldCount > 0) {
 | 
				
			||||||
 | 
					            fields = new String[fieldCount];
 | 
				
			||||||
 | 
					            fieldIds = new int[fieldCount];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (int i = 0; i < fieldCount; i++) {
 | 
				
			||||||
 | 
					                fieldIds[i] = din.readUnsignedByte();
 | 
				
			||||||
 | 
					                int len = din.readUnsignedByte();
 | 
				
			||||||
 | 
					                byte data[] = new byte[len];
 | 
				
			||||||
 | 
					                din.readFully(data);
 | 
				
			||||||
 | 
					                fields[i] = new String(data, "UTF-8");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                byteCount += 2 + len;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            setDataLength(byteCount);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            setDataLength(byteCount);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        int size = 6;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (int i = 0; i < fieldCount; i++) {
 | 
				
			||||||
 | 
					            // Add the field ID
 | 
				
			||||||
 | 
					            size++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Add the string length byte and the
 | 
				
			||||||
 | 
					            // actual string length.
 | 
				
			||||||
 | 
					            size++;
 | 
				
			||||||
 | 
					            size += fields[i].length();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        byte result[] = new byte[size];
 | 
				
			||||||
 | 
					        int offset = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result[offset++] = 0x0E;
 | 
				
			||||||
 | 
					        result[offset++] = (byte)fieldLevel;
 | 
				
			||||||
 | 
					        result[offset++] = (byte)conformity;
 | 
				
			||||||
 | 
					        result[offset++] = (byte)(moreFollows ? 0xFF : 0);
 | 
				
			||||||
 | 
					        result[offset++] = (byte)nextFieldId;
 | 
				
			||||||
 | 
					        result[offset++] = (byte)fieldCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (int i = 0; i < fieldCount; i++) {
 | 
				
			||||||
 | 
					            result[offset++] = (byte)fieldIds[i];
 | 
				
			||||||
 | 
					            result[offset++] = (byte)fields[i].length();
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                System.arraycopy(fields[i].getBytes("US-ASCII"), 0, result, offset, fields[i].length());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (Exception e) {
 | 
				
			||||||
 | 
					                logger.debug("Problem converting bytes to string - {}", e.getMessage());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            offset += fields[i].length();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,163 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractModbusListener;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.IllegalAddressException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.ProcessImage;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.Register;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>ReadMultipleRegistersRequest</tt>. The
 | 
				
			||||||
 | 
					 * implementation directly correlates with the class 0 function <i>read multiple
 | 
				
			||||||
 | 
					 * registers (FC 3)</i>. It encapsulates the corresponding request message.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReadMultipleRegistersRequest extends ModbusRequest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // instance attributes
 | 
				
			||||||
 | 
					    private int reference;
 | 
				
			||||||
 | 
					    private int wordCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ReadMultipleRegistersRequest</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadMultipleRegistersRequest() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_MULTIPLE_REGISTERS);
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ReadMultipleRegistersRequest</tt> instance with a
 | 
				
			||||||
 | 
					     * given reference and count of words to be read.  This message reads
 | 
				
			||||||
 | 
					     * from holding (r/w) registers.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref   the reference number of the register to read from.
 | 
				
			||||||
 | 
					     * @param count the number of words to be read.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @see ReadInputRegistersRequest
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadMultipleRegistersRequest(int ref, int count) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_MULTIPLE_REGISTERS);
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setReference(ref);
 | 
				
			||||||
 | 
					        setWordCount(count);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse getResponse() {
 | 
				
			||||||
 | 
					        return updateResponseWithHeader(new ReadMultipleRegistersResponse());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse createResponse(AbstractModbusListener listener) {
 | 
				
			||||||
 | 
					        ReadMultipleRegistersResponse response;
 | 
				
			||||||
 | 
					        Register[] regs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 1. get process image
 | 
				
			||||||
 | 
					        ProcessImage procimg = listener.getProcessImage(getUnitID());
 | 
				
			||||||
 | 
					        // 2. get input registers range
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            regs = procimg.getRegisterRange(getReference(), getWordCount());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (IllegalAddressException e) {
 | 
				
			||||||
 | 
					            return createExceptionResponse(Modbus.ILLEGAL_ADDRESS_EXCEPTION);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        response = (ReadMultipleRegistersResponse)getResponse();
 | 
				
			||||||
 | 
					        response.setRegisters(regs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the reference of the register to to start reading from with this
 | 
				
			||||||
 | 
					     * <tt>ReadMultipleRegistersRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the reference of the register to start reading from as
 | 
				
			||||||
 | 
					     * <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getReference() {
 | 
				
			||||||
 | 
					        return reference;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the reference of the register to start reading from with this
 | 
				
			||||||
 | 
					     * <tt>ReadMultipleRegistersRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref the reference of the register to start reading from.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setReference(int ref) {
 | 
				
			||||||
 | 
					        reference = ref;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the number of words to be read with this
 | 
				
			||||||
 | 
					     * <tt>ReadMultipleRegistersRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of words to be read as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getWordCount() {
 | 
				
			||||||
 | 
					        return wordCount;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the number of words to be read with this
 | 
				
			||||||
 | 
					     * <tt>ReadMultipleRegistersRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param count the number of words to be read.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setWordCount(int count) {
 | 
				
			||||||
 | 
					        wordCount = count;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.writeShort(reference);
 | 
				
			||||||
 | 
					        dout.writeShort(wordCount);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        reference = din.readUnsignedShort();
 | 
				
			||||||
 | 
					        wordCount = din.readUnsignedShort();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte result[] = new byte[4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result[0] = (byte)((reference >> 8) & 0xff);
 | 
				
			||||||
 | 
					        result[1] = (byte)(reference & 0xff);
 | 
				
			||||||
 | 
					        result[2] = (byte)((wordCount >> 8) & 0xff);
 | 
				
			||||||
 | 
					        result[3] = (byte)(wordCount & 0xff);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,184 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.Register;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.SimpleRegister;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.util.Arrays;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>ReadMultipleRegistersResponse</tt>. The
 | 
				
			||||||
 | 
					 * implementation directly correlates with the class 0 function <i>read multiple
 | 
				
			||||||
 | 
					 * registers (FC 3)</i>. It encapsulates the corresponding response message.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Julie (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReadMultipleRegistersResponse extends ModbusResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // instance attributes
 | 
				
			||||||
 | 
					    private int byteCount;
 | 
				
			||||||
 | 
					    private Register[] registers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ReadMultipleRegistersResponse</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadMultipleRegistersResponse() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_MULTIPLE_REGISTERS);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ReadInputRegistersResponse</tt> instance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param registers the Register[] holding response registers.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadMultipleRegistersResponse(Register[] registers) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_MULTIPLE_REGISTERS);
 | 
				
			||||||
 | 
					        setDataLength(registers == null ? 0 : (registers.length * 2 + 1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.registers = registers == null ? null : Arrays.copyOf(registers, registers.length);
 | 
				
			||||||
 | 
					        byteCount = registers == null ? 0 : (registers.length * 2);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the number of bytes that have been read.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of bytes that have been read as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getByteCount() {
 | 
				
			||||||
 | 
					        return byteCount;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the number of words that have been read. The returned value
 | 
				
			||||||
 | 
					     * should be half of the the byte count of this
 | 
				
			||||||
 | 
					     * <tt>ReadMultipleRegistersResponse</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of words that have been read as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getWordCount() {
 | 
				
			||||||
 | 
					        return byteCount / 2;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the <tt>Register</tt> at the given position (relative to the
 | 
				
			||||||
 | 
					     * reference used in the request).
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param index the relative index of the <tt>Register</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the register as <tt>Register</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IndexOutOfBoundsException if the index is out of bounds.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public Register getRegister(int index) {
 | 
				
			||||||
 | 
					        if (registers == null) {
 | 
				
			||||||
 | 
					            throw new IndexOutOfBoundsException("No registers defined!");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (index < 0) {
 | 
				
			||||||
 | 
					            throw new IndexOutOfBoundsException("Negative index: " + index);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (index >= getWordCount()) {
 | 
				
			||||||
 | 
					            throw new IndexOutOfBoundsException(index + " > " + getWordCount());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return registers[index];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the value of the register at the given position (relative to the
 | 
				
			||||||
 | 
					     * reference used in the request) interpreted as unsigned short.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param index the relative index of the register for which the value should
 | 
				
			||||||
 | 
					     *              be retrieved.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the value as <tt>int</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IndexOutOfBoundsException if the index is out of bounds.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getRegisterValue(int index) throws IndexOutOfBoundsException {
 | 
				
			||||||
 | 
					        return getRegister(index).toUnsignedShort();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the reference to the array of registers read.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a <tt>Register[]</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized Register[] getRegisters() {
 | 
				
			||||||
 | 
					        Register[] dest = new Register[registers.length];
 | 
				
			||||||
 | 
					        System.arraycopy(registers, 0, dest, 0, dest.length);
 | 
				
			||||||
 | 
					        return dest;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the entire block of registers for this response
 | 
				
			||||||
 | 
					     * @param registers Array of registers to use
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setRegisters(Register[] registers) {
 | 
				
			||||||
 | 
					        byteCount = registers == null ? 0 : registers.length * 2;
 | 
				
			||||||
 | 
					        this.registers = registers == null ? null : Arrays.copyOf(registers, registers.length);
 | 
				
			||||||
 | 
					        setDataLength(byteCount + 1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.writeByte(byteCount);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (int k = 0; k < getWordCount(); k++) {
 | 
				
			||||||
 | 
					            dout.write(registers[k].toBytes());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        byteCount = din.readUnsignedByte();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        registers = new Register[getWordCount()];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (int k = 0; k < getWordCount(); k++) {
 | 
				
			||||||
 | 
					            registers[k] = new SimpleRegister(din.readByte(), din.readByte());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setDataLength(byteCount + 1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte result[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result = new byte[getWordCount() * 2 + 1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int offset = 0;
 | 
				
			||||||
 | 
					        result[offset++] = (byte)byteCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (Register register : registers) {
 | 
				
			||||||
 | 
					            byte[] data = register.toBytes();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            result[offset++] = data[0];
 | 
				
			||||||
 | 
					            result[offset++] = data[1];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,168 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractModbusListener;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>ReadSerialDiagnosticsRequest</tt>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Julie Haugh (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReadSerialDiagnosticsRequest extends ModbusRequest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Message fields.
 | 
				
			||||||
 | 
					    private int function;
 | 
				
			||||||
 | 
					    private short data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>Diagnostics</tt> request
 | 
				
			||||||
 | 
					     * instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadSerialDiagnosticsRequest() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_SERIAL_DIAGNOSTICS);
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getFunction -- Get the DIAGNOSTICS sub-function.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return int
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getFunction() {
 | 
				
			||||||
 | 
					        return function;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setFunction - Set the DIAGNOSTICS sub-function.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param function - DIAGNOSTICS command sub-function.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setFunction(int function) {
 | 
				
			||||||
 | 
					        this.function = function;
 | 
				
			||||||
 | 
					        data = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getWordCount -- get the number of words in data.
 | 
				
			||||||
 | 
					     * @return Number of words in the data
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getWordCount() {
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getData
 | 
				
			||||||
 | 
					     * @return the first data item
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getData() {
 | 
				
			||||||
 | 
					        return data;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setData -- Set the optional data value
 | 
				
			||||||
 | 
					     * @param value Diagnostics value
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setData(int value) {
 | 
				
			||||||
 | 
					        data = (short)value;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getData -- Get the data item at the index.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param index - Unused, must be 0.
 | 
				
			||||||
 | 
					     * @return Data at index 0
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @deprecated
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getData(int index) {
 | 
				
			||||||
 | 
					        if (index != 0) {
 | 
				
			||||||
 | 
					            throw new IndexOutOfBoundsException();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return data;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setData -- Set the data item at the index
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param index - Unused, must be 0.
 | 
				
			||||||
 | 
					     * @param value - Optional data value for function.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @deprecated
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setData(int index, int value) {
 | 
				
			||||||
 | 
					        if (index != 0) {
 | 
				
			||||||
 | 
					            throw new IndexOutOfBoundsException();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        data = (short)value;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse getResponse() {
 | 
				
			||||||
 | 
					        ReadSerialDiagnosticsResponse response = new ReadSerialDiagnosticsResponse();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Copy the sub-function code.
 | 
				
			||||||
 | 
					        response.setFunction(getFunction());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return updateResponseWithHeader(response);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse createResponse(AbstractModbusListener listener) {
 | 
				
			||||||
 | 
					        return createExceptionResponse(Modbus.ILLEGAL_FUNCTION_EXCEPTION);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * writeData -- output the completed Modbus message to dout
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be written
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.write(getMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * readData -- Read the function code and data value
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be read
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        function = din.readUnsignedShort();
 | 
				
			||||||
 | 
					        data = (short)(din.readShort() & 0xFFFF);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getMessage -- Create the DIAGNOSTICS message paylaod.
 | 
				
			||||||
 | 
					     * @return Response as byte array
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte result[] = new byte[4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result[0] = (byte)(function >> 8);
 | 
				
			||||||
 | 
					        result[1] = (byte)(function & 0xFF);
 | 
				
			||||||
 | 
					        result[2] = (byte)(data >> 8);
 | 
				
			||||||
 | 
					        result[3] = (byte)(data & 0xFF);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,154 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>ReadSerialDiagnosticsResponse</tt>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Julie Haugh (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReadSerialDiagnosticsResponse extends ModbusResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Message fields.
 | 
				
			||||||
 | 
					    private int function;
 | 
				
			||||||
 | 
					    private short data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>Diagnostics</tt> response
 | 
				
			||||||
 | 
					     * instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadSerialDiagnosticsResponse() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_SERIAL_DIAGNOSTICS);
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getFunction -- Get the DIAGNOSTICS sub-function.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Function code
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getFunction() {
 | 
				
			||||||
 | 
					        return function;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setFunction - Set the DIAGNOSTICS sub-function.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param function - DIAGNOSTICS command sub-function.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setFunction(int function) {
 | 
				
			||||||
 | 
					        this.function = function;
 | 
				
			||||||
 | 
					        data = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getWordCount -- get the number of words in data.
 | 
				
			||||||
 | 
					     * @return Number of words in the data
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getWordCount() {
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getData
 | 
				
			||||||
 | 
					     * @return the first data item
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getData() {
 | 
				
			||||||
 | 
					        return data;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setData -- Set the optional data value
 | 
				
			||||||
 | 
					     * @param value optional data value
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setData(int value) {
 | 
				
			||||||
 | 
					        data = (short)value;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getData -- Get the data item at the index.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param index - Unused, must be 0.
 | 
				
			||||||
 | 
					     * @return Data at index 0
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @deprecated
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getData(int index) {
 | 
				
			||||||
 | 
					        if (index != 0) {
 | 
				
			||||||
 | 
					            throw new IndexOutOfBoundsException();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return data;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setData -- Set the data item at the index
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param index - Unused, must be 0.
 | 
				
			||||||
 | 
					     * @param value - Optional data value for function.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @deprecated
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setData(int index, int value) {
 | 
				
			||||||
 | 
					        if (index != 0) {
 | 
				
			||||||
 | 
					            throw new IndexOutOfBoundsException();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data = (short)value;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * writeData -- output the completed Modbus message to dout
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be written
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.write(getMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * readData -- Read the function code and data value
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be read
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        function = din.readUnsignedShort();
 | 
				
			||||||
 | 
					        data = (short)(din.readShort() & 0xFFFF);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getMessage -- Create the DIAGNOSTICS message paylaod.
 | 
				
			||||||
 | 
					     * @return message paylaod
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte result[] = new byte[4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result[0] = (byte)(function >> 8);
 | 
				
			||||||
 | 
					        result[1] = (byte)(function & 0xFF);
 | 
				
			||||||
 | 
					        result[2] = (byte)(data >> 8);
 | 
				
			||||||
 | 
					        result[3] = (byte)(data & 0xFF);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,372 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.io.NonWordDataHandler;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractModbusListener;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.util.Arrays;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>Read / Write Multiple Registers</tt> request.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Julie Haugh
 | 
				
			||||||
 | 
					 * @author Julie Haugh
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReadWriteMultipleRequest extends ModbusRequest {
 | 
				
			||||||
 | 
					    private NonWordDataHandler nonWordDataHandler;
 | 
				
			||||||
 | 
					    private int readReference;
 | 
				
			||||||
 | 
					    private int readCount;
 | 
				
			||||||
 | 
					    private int writeReference;
 | 
				
			||||||
 | 
					    private int writeCount;
 | 
				
			||||||
 | 
					    private Register registers[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>Read/Write Multiple Registers Request</tt> instance.
 | 
				
			||||||
 | 
					     * @param unit Unit ID
 | 
				
			||||||
 | 
					     * @param readRef Register to read
 | 
				
			||||||
 | 
					     * @param writeCount Number of registers to write
 | 
				
			||||||
 | 
					     * @param writeRef Starting register to write
 | 
				
			||||||
 | 
					     * @param readCount Number of registers to read
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadWriteMultipleRequest(int unit, int readRef, int readCount, int writeRef, int writeCount) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setUnitID(unit);
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_WRITE_MULTIPLE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // There is no additional data in this request.
 | 
				
			||||||
 | 
					        setDataLength(9 + writeCount * 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        readReference = readRef;
 | 
				
			||||||
 | 
					        this.readCount = readCount;
 | 
				
			||||||
 | 
					        writeReference = writeRef;
 | 
				
			||||||
 | 
					        this.writeCount = writeCount;
 | 
				
			||||||
 | 
					        registers = new Register[writeCount];
 | 
				
			||||||
 | 
					        for (int i = 0; i < writeCount; i++) {
 | 
				
			||||||
 | 
					            registers[i] = new SimpleRegister(0);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>Read/Write Multiple Registers Request</tt> instance.
 | 
				
			||||||
 | 
					     * @param unit Unit ID
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadWriteMultipleRequest(int unit) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setUnitID(unit);
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_WRITE_MULTIPLE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // There is no additional data in this request.
 | 
				
			||||||
 | 
					        setDataLength(9);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>Read/Write Multiple Registers Request</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadWriteMultipleRequest() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_WRITE_MULTIPLE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // There is no additional data in this request.
 | 
				
			||||||
 | 
					        setDataLength(9);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse getResponse() {
 | 
				
			||||||
 | 
					        return updateResponseWithHeader(new ReadWriteMultipleResponse());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse createResponse(AbstractModbusListener listener) {
 | 
				
			||||||
 | 
					        ReadWriteMultipleResponse response;
 | 
				
			||||||
 | 
					        InputRegister[] readRegs;
 | 
				
			||||||
 | 
					        Register[] writeRegs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 1. get process image
 | 
				
			||||||
 | 
					        ProcessImage procimg = listener.getProcessImage(getUnitID());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 2. get input registers range
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            // First the write
 | 
				
			||||||
 | 
					            writeRegs = procimg.getRegisterRange(getWriteReference(), getWriteWordCount());
 | 
				
			||||||
 | 
					            for (int i = 0; i < writeRegs.length; i++) {
 | 
				
			||||||
 | 
					                writeRegs[i].setValue(getRegister(i).getValue());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // And then the read
 | 
				
			||||||
 | 
					            readRegs = procimg.getRegisterRange(getReadReference(), getReadWordCount());
 | 
				
			||||||
 | 
					            InputRegister[] dummy = new InputRegister[readRegs.length];
 | 
				
			||||||
 | 
					            for (int i = 0; i < readRegs.length; i++) {
 | 
				
			||||||
 | 
					                dummy[i] = new SimpleInputRegister(readRegs[i].getValue());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            readRegs = dummy;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (IllegalAddressException e) {
 | 
				
			||||||
 | 
					            return createExceptionResponse(Modbus.ILLEGAL_ADDRESS_EXCEPTION);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        response = (ReadWriteMultipleResponse)getResponse();
 | 
				
			||||||
 | 
					        response.setRegisters(readRegs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getReadReference - Returns the reference of the register to start writing
 | 
				
			||||||
 | 
					     * to with this <tt>ReadWriteMultipleRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the reference of the register to start writing to as <tt>int</tt>
 | 
				
			||||||
 | 
					     * .
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getReadReference() {
 | 
				
			||||||
 | 
					        return readReference;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setReadReference - Sets the reference of the register to writing to with
 | 
				
			||||||
 | 
					     * this <tt>ReadWriteMultipleRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref the reference of the register to start writing to as
 | 
				
			||||||
 | 
					     *            <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setReadReference(int ref) {
 | 
				
			||||||
 | 
					        readReference = ref;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getWriteReference - Returns the reference of the register to start
 | 
				
			||||||
 | 
					     * writing to with this <tt>ReadWriteMultipleRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the reference of the register to start writing to as <tt>int</tt>
 | 
				
			||||||
 | 
					     * .
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getWriteReference() {
 | 
				
			||||||
 | 
					        return writeReference;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setWriteReference - Sets the reference of the register to write to with
 | 
				
			||||||
 | 
					     * this <tt>ReadWriteMultipleRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref the reference of the register to start writing to as
 | 
				
			||||||
 | 
					     *            <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setWriteReference(int ref) {
 | 
				
			||||||
 | 
					        writeReference = ref;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getRegisters - Returns the registers to be written with this
 | 
				
			||||||
 | 
					     * <tt>ReadWriteMultipleRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the registers to be read as <tt>Register[]</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized Register[] getRegisters() {
 | 
				
			||||||
 | 
					        Register[] dest = new Register[registers.length];
 | 
				
			||||||
 | 
					        System.arraycopy(registers, 0, dest, 0, dest.length);
 | 
				
			||||||
 | 
					        return dest;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setRegisters - Sets the registers to be written with this
 | 
				
			||||||
 | 
					     * <tt>ReadWriteMultipleRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param registers the registers to be written as <tt>Register[]</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setRegisters(Register[] registers) {
 | 
				
			||||||
 | 
					        writeCount = registers != null ? registers.length : 0;
 | 
				
			||||||
 | 
					        this.registers = registers != null ? Arrays.copyOf(registers, registers.length) : null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getRegister - Returns the specified <tt>Register</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param index the index of the <tt>Register</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the register as <tt>Register</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IndexOutOfBoundsException if the index is out of bounds.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public Register getRegister(int index) throws IndexOutOfBoundsException {
 | 
				
			||||||
 | 
					        if (index < 0) {
 | 
				
			||||||
 | 
					            throw new IndexOutOfBoundsException(index + " < 0");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (index >= getWriteWordCount()) {
 | 
				
			||||||
 | 
					            throw new IndexOutOfBoundsException(index + " > " + getWriteWordCount());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return registers[index];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getReadRegisterValue - Returns the value of the specified register
 | 
				
			||||||
 | 
					     * interpreted as unsigned short.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param index the relative index of the register for which the value should
 | 
				
			||||||
 | 
					     *              be retrieved.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the value as <tt>int</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IndexOutOfBoundsException if the index is out of bounds.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getReadRegisterValue(int index) throws IndexOutOfBoundsException {
 | 
				
			||||||
 | 
					        return getRegister(index).toUnsignedShort();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getByteCount - Returns the number of bytes representing the values to be
 | 
				
			||||||
 | 
					     * written.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of bytes to be written as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getByteCount() {
 | 
				
			||||||
 | 
					        return getWriteWordCount() * 2;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getWriteWordCount - Returns the number of words to be written.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of words to be written as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getWriteWordCount() {
 | 
				
			||||||
 | 
					        return writeCount;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setWriteWordCount - Sets the number of words to be written.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param count the number of words to be written as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setWriteWordCount(int count) {
 | 
				
			||||||
 | 
					        writeCount = count;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getReadWordCount - Returns the number of words to be read.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of words to be read as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getReadWordCount() {
 | 
				
			||||||
 | 
					        return readCount;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setReadWordCount - Sets the number of words to be read.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param count the number of words to be read as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setReadWordCount(int count) {
 | 
				
			||||||
 | 
					        readCount = count;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getNonWordDataHandler - Returns the actual non word data handler.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the actual <tt>NonWordDataHandler</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public NonWordDataHandler getNonWordDataHandler() {
 | 
				
			||||||
 | 
					        return nonWordDataHandler;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setNonWordDataHandler - Sets a non word data handler. A non-word data
 | 
				
			||||||
 | 
					     * handler is responsible for converting words from a Modbus packet into the
 | 
				
			||||||
 | 
					     * non-word values associated with the actual device's registers.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param dhandler a <tt>NonWordDataHandler</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setNonWordDataHandler(NonWordDataHandler dhandler) {
 | 
				
			||||||
 | 
					        nonWordDataHandler = dhandler;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * writeData -- output this Modbus message to dout.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.write(getMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * readData -- read the values of the registers to be written, along with
 | 
				
			||||||
 | 
					     * the reference and count for the registers to be read.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void readData(DataInput input) throws IOException {
 | 
				
			||||||
 | 
					        readReference = input.readUnsignedShort();
 | 
				
			||||||
 | 
					        readCount = input.readUnsignedShort();
 | 
				
			||||||
 | 
					        writeReference = input.readUnsignedShort();
 | 
				
			||||||
 | 
					        writeCount = input.readUnsignedShort();
 | 
				
			||||||
 | 
					        int byteCount = input.readUnsignedByte();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (nonWordDataHandler == null) {
 | 
				
			||||||
 | 
					            byte buffer[] = new byte[byteCount];
 | 
				
			||||||
 | 
					            input.readFully(buffer, 0, byteCount);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            int offset = 0;
 | 
				
			||||||
 | 
					            registers = new Register[writeCount];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (int register = 0; register < writeCount; register++) {
 | 
				
			||||||
 | 
					                registers[register] = new SimpleRegister(buffer[offset], buffer[offset + 1]);
 | 
				
			||||||
 | 
					                offset += 2;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            nonWordDataHandler.readData(input, writeReference, writeCount);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getMessage -- return a prepared message.
 | 
				
			||||||
 | 
					     * @return prepared message
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte results[] = new byte[9 + 2 * getWriteWordCount()];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        results[0] = (byte)(readReference >> 8);
 | 
				
			||||||
 | 
					        results[1] = (byte)(readReference & 0xFF);
 | 
				
			||||||
 | 
					        results[2] = (byte)(readCount >> 8);
 | 
				
			||||||
 | 
					        results[3] = (byte)(readCount & 0xFF);
 | 
				
			||||||
 | 
					        results[4] = (byte)(writeReference >> 8);
 | 
				
			||||||
 | 
					        results[5] = (byte)(writeReference & 0xFF);
 | 
				
			||||||
 | 
					        results[6] = (byte)(writeCount >> 8);
 | 
				
			||||||
 | 
					        results[7] = (byte)(writeCount & 0xFF);
 | 
				
			||||||
 | 
					        results[8] = (byte)(writeCount * 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int offset = 9;
 | 
				
			||||||
 | 
					        for (int i = 0; i < writeCount; i++) {
 | 
				
			||||||
 | 
					            Register reg = getRegister(i);
 | 
				
			||||||
 | 
					            byte[] bytes = reg.toBytes();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            results[offset++] = bytes[0];
 | 
				
			||||||
 | 
					            results[offset++] = bytes[1];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return results;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,198 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.InputRegister;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.Register;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.SimpleRegister;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.util.Arrays;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>ReadWriteMultipleResponse</tt>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Julie (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReadWriteMultipleResponse extends ModbusResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private int byteCount;
 | 
				
			||||||
 | 
					    private InputRegister[] registers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ReadWriteMultipleResponse</tt> instance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param registers the Register[] holding response registers.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadWriteMultipleResponse(InputRegister[] registers) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_WRITE_MULTIPLE);
 | 
				
			||||||
 | 
					        setDataLength(registers.length * 2 + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.registers = Arrays.copyOf(registers, registers.length);
 | 
				
			||||||
 | 
					        byteCount = registers.length * 2;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ReadWriteMultipleResponse</tt> instance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param count the number of Register[] holding response registers.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadWriteMultipleResponse(int count) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_WRITE_MULTIPLE);
 | 
				
			||||||
 | 
					        setDataLength(count * 2 + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        registers = new InputRegister[count];
 | 
				
			||||||
 | 
					        byteCount = count * 2;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ReadWriteMultipleResponse</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReadWriteMultipleResponse() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.READ_WRITE_MULTIPLE);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the number of bytes that have been read.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of bytes that have been read as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getByteCount() {
 | 
				
			||||||
 | 
					        return byteCount;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the number of words that have been read. The returned value
 | 
				
			||||||
 | 
					     * should be half of the the byte count of this
 | 
				
			||||||
 | 
					     * <tt>ReadWriteMultipleResponse</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of words that have been read as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getWordCount() {
 | 
				
			||||||
 | 
					        return byteCount / 2;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the <tt>Register</tt> at the given position (relative to the
 | 
				
			||||||
 | 
					     * reference used in the request).
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param index the relative index of the <tt>InputRegister</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the register as <tt>InputRegister</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IndexOutOfBoundsException if the index is out of bounds.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public InputRegister getRegister(int index) {
 | 
				
			||||||
 | 
					        if (registers == null) {
 | 
				
			||||||
 | 
					            throw new IndexOutOfBoundsException("No registers defined!");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (index < 0) {
 | 
				
			||||||
 | 
					            throw new IndexOutOfBoundsException("Negative index: " + index);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (index >= getWordCount()) {
 | 
				
			||||||
 | 
					            throw new IndexOutOfBoundsException(index + " > " + getWordCount());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return registers[index];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the value of the register at the given position (relative to the
 | 
				
			||||||
 | 
					     * reference used in the request) interpreted as unsigned short.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param index the relative index of the register for which the value should
 | 
				
			||||||
 | 
					     *              be retrieved.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the value as <tt>int</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IndexOutOfBoundsException if the index is out of bounds.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getRegisterValue(int index) throws IndexOutOfBoundsException {
 | 
				
			||||||
 | 
					        return getRegister(index).toUnsignedShort();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the reference to the array of registers read.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a <tt>InputRegister[]</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized InputRegister[] getRegisters() {
 | 
				
			||||||
 | 
					        InputRegister[] dest = new InputRegister[registers.length];
 | 
				
			||||||
 | 
					        System.arraycopy(registers, 0, dest, 0, dest.length);
 | 
				
			||||||
 | 
					        return dest;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the entire block of registers for this response
 | 
				
			||||||
 | 
					     * @param registers Array of registers
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setRegisters(InputRegister[] registers) {
 | 
				
			||||||
 | 
					        byteCount = registers.length * 2;
 | 
				
			||||||
 | 
					        setDataLength(byteCount + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.registers = Arrays.copyOf(registers, registers.length);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.writeByte(byteCount);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (int k = 0; k < getWordCount(); k++) {
 | 
				
			||||||
 | 
					            dout.write(registers[k].toBytes());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        byteCount = din.readUnsignedByte();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        registers = new Register[getWordCount()];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (int k = 0; k < getWordCount(); k++) {
 | 
				
			||||||
 | 
					            registers[k] = new SimpleRegister(din.readByte(), din.readByte());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setDataLength(byteCount + 1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte result[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result = new byte[getWordCount() * 2 + 1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int offset = 0;
 | 
				
			||||||
 | 
					        result[offset++] = (byte)byteCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (InputRegister register : registers) {
 | 
				
			||||||
 | 
					            byte[] data = register.toBytes();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            result[offset++] = data[0];
 | 
				
			||||||
 | 
					            result[offset++] = data[1];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,81 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractModbusListener;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>Read MEI Data</tt> request.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Julie Haugh (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author jfhaugh (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReportSlaveIDRequest extends ModbusRequest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>Report Slave ID request</tt>
 | 
				
			||||||
 | 
					     * instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReportSlaveIDRequest() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.REPORT_SLAVE_ID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // There is no additional data in this request.
 | 
				
			||||||
 | 
					        setDataLength(0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse getResponse() {
 | 
				
			||||||
 | 
					        return updateResponseWithHeader(new ReportSlaveIDResponse());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse createResponse(AbstractModbusListener listener) {
 | 
				
			||||||
 | 
					        return createExceptionResponse(Modbus.ILLEGAL_FUNCTION_EXCEPTION);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * writeData -- output this Modbus message to dout.
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be written
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.write(getMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * readData -- dummy function.  There is no data with the request.
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be read
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getMessage
 | 
				
			||||||
 | 
					     * @return an empty array as there is no data for this request
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return new byte[0];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,178 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>ReadMEIResponse</tt>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Derived from similar class for Read Coils response.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Julie Haugh (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class ReportSlaveIDResponse extends ModbusResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Message fields.
 | 
				
			||||||
 | 
					    int m_length;
 | 
				
			||||||
 | 
					    byte m_data[];
 | 
				
			||||||
 | 
					    int m_status;
 | 
				
			||||||
 | 
					    int m_slaveId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>ReportSlaveIDResponse</tt>
 | 
				
			||||||
 | 
					     * instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ReportSlaveIDResponse() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.REPORT_SLAVE_ID);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getSlaveID -- return the slave identifier field.
 | 
				
			||||||
 | 
					     * @return slave identifier field
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getSlaveID() {
 | 
				
			||||||
 | 
					        return m_slaveId;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setSlaveID -- initialize the slave identifier when constructing
 | 
				
			||||||
 | 
					     * a response message.
 | 
				
			||||||
 | 
					     * @param unitID UnitID of the slave
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setSlaveID(int unitID) {
 | 
				
			||||||
 | 
					        m_slaveId = unitID;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getStatus -- get the slave's "run" status.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return boolean
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean getStatus() {
 | 
				
			||||||
 | 
					        return m_status != 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setStatus -- initialize the slave's "run" status when constructing
 | 
				
			||||||
 | 
					     * a response message.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param b Status value
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setStatus(boolean b) {
 | 
				
			||||||
 | 
					        m_status = b ? 0xff : 0x00;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getData -- get the device-depending data for the slave.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return byte array
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public byte[] getData() {
 | 
				
			||||||
 | 
					        byte[] result = new byte[m_length - 2];
 | 
				
			||||||
 | 
					        System.arraycopy(m_data, 0, result, 0, m_length - 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setData -- initialize the slave's device dependent data when
 | 
				
			||||||
 | 
					     * initializing a response.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param data byte array
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setData(byte[] data) {
 | 
				
			||||||
 | 
					        // There are always two bytes of payload in the message -- the
 | 
				
			||||||
 | 
					        // slave ID and the run status indicator.
 | 
				
			||||||
 | 
					        if (data == null) {
 | 
				
			||||||
 | 
					            m_length = 2;
 | 
				
			||||||
 | 
					            m_data = new byte[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (data.length > 249) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException("data length limit exceeded");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        m_length = data.length + 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        m_data = new byte[data.length];
 | 
				
			||||||
 | 
					        System.arraycopy(data, 0, m_data, 0, data.length);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * writeData -- output the completed Modbus message to dout
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be written
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.write(getMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * readData -- input the Modbus message from din.  If there was a
 | 
				
			||||||
 | 
					     * header, such as for Modbus/TCP, it will have been read
 | 
				
			||||||
 | 
					     * already.
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be read
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Get the size of any device-specific data.
 | 
				
			||||||
 | 
					        m_length = din.readUnsignedByte();
 | 
				
			||||||
 | 
					        if (m_length < 2 || m_length > 255) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Get the run status and device identifier.
 | 
				
			||||||
 | 
					        m_slaveId = din.readUnsignedByte();
 | 
				
			||||||
 | 
					        m_status = din.readUnsignedByte();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /*
 | 
				
			||||||
 | 
					         * The device-specific data is two bytes shorter than the
 | 
				
			||||||
 | 
					         * length read previously.  That length includes the run status
 | 
				
			||||||
 | 
					         * and slave ID.
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        m_data = new byte[m_length - 2];
 | 
				
			||||||
 | 
					        if (m_length > 2) {
 | 
				
			||||||
 | 
					            din.readFully(m_data, 0, m_length - 2);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getMessage -- format the message into a byte array.
 | 
				
			||||||
 | 
					     * @return Byte array of message
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte result[] = new byte[3 + m_length];
 | 
				
			||||||
 | 
					        int offset = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result[offset++] = (byte)(m_length + 2);
 | 
				
			||||||
 | 
					        result[offset++] = (byte)m_slaveId;
 | 
				
			||||||
 | 
					        result[offset++] = (byte)m_status;
 | 
				
			||||||
 | 
					        if (m_length > 0) {
 | 
				
			||||||
 | 
					            System.arraycopy(m_data, 0, result, offset, m_length - 2);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,178 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractModbusListener;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.DigitalOut;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.IllegalAddressException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.ProcessImage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>WriteCoilRequest</tt>. The implementation directly
 | 
				
			||||||
 | 
					 * correlates with the class 0 function <i>write coil (FC 5)</i>. It
 | 
				
			||||||
 | 
					 * encapsulates the corresponding request message.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class WriteCoilRequest extends ModbusRequest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // instance attributes
 | 
				
			||||||
 | 
					    private int reference;
 | 
				
			||||||
 | 
					    private boolean coil;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>WriteCoilRequest</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public WriteCoilRequest() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.WRITE_COIL);
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>WriteCoilRequest</tt> instance with a given
 | 
				
			||||||
 | 
					     * reference and state to be written.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref the reference number of the register to read from.
 | 
				
			||||||
 | 
					     * @param b   true if the coil should be set of false if it should be unset.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public WriteCoilRequest(int ref, boolean b) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.WRITE_COIL);
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setReference(ref);
 | 
				
			||||||
 | 
					        setCoil(b);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse getResponse() {
 | 
				
			||||||
 | 
					        return updateResponseWithHeader(new WriteCoilResponse());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse createResponse(AbstractModbusListener listener) {
 | 
				
			||||||
 | 
					        WriteCoilResponse response;
 | 
				
			||||||
 | 
					        DigitalOut dout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 1. get process image
 | 
				
			||||||
 | 
					        ProcessImage procimg = listener.getProcessImage(getUnitID());
 | 
				
			||||||
 | 
					        // 2. get coil
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            dout = procimg.getDigitalOut(getReference());
 | 
				
			||||||
 | 
					            // 3. set coil
 | 
				
			||||||
 | 
					            dout.set(getCoil());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (IllegalAddressException iaex) {
 | 
				
			||||||
 | 
					            return createExceptionResponse(Modbus.ILLEGAL_ADDRESS_EXCEPTION);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        response = (WriteCoilResponse)getResponse();
 | 
				
			||||||
 | 
					        response.setReference(getReference());
 | 
				
			||||||
 | 
					        response.setCoil(getCoil());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the reference of the register of the coil that should be written
 | 
				
			||||||
 | 
					     * to with this <tt>ReadCoilsRequest</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the reference of the coil's register.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getReference() {
 | 
				
			||||||
 | 
					        return reference;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the reference of the register of the coil that should be written to
 | 
				
			||||||
 | 
					     * with this <tt>ReadCoilsRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref the reference of the coil's register.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setReference(int ref) {
 | 
				
			||||||
 | 
					        reference = ref;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the state that should be written with this
 | 
				
			||||||
 | 
					     * <tt>WriteCoilRequest</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return true if the coil should be set of false if it should be unset.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean getCoil() {
 | 
				
			||||||
 | 
					        return coil;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the state that should be written with this <tt>WriteCoilRequest</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param b true if the coil should be set of false if it should be unset.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setCoil(boolean b) {
 | 
				
			||||||
 | 
					        coil = b;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.writeShort(reference);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (coil) {
 | 
				
			||||||
 | 
					            dout.write(Modbus.COIL_ON_BYTES, 0, 2);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            dout.write(Modbus.COIL_OFF_BYTES, 0, 2);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        reference = din.readUnsignedShort();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (din.readByte() == Modbus.COIL_ON) {
 | 
				
			||||||
 | 
					            coil = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            coil = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // discard the next byte.
 | 
				
			||||||
 | 
					        din.readByte();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte result[] = new byte[4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result[0] = (byte)((reference >> 8) & 0xff);
 | 
				
			||||||
 | 
					        result[1] = (byte)(reference & 0xff);
 | 
				
			||||||
 | 
					        if (coil) {
 | 
				
			||||||
 | 
					            result[2] = Modbus.COIL_ON_BYTES[0];
 | 
				
			||||||
 | 
					            result[3] = Modbus.COIL_ON_BYTES[1];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            result[2] = Modbus.COIL_OFF_BYTES[0];
 | 
				
			||||||
 | 
					            result[3] = Modbus.COIL_OFF_BYTES[1];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,132 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>WriteCoilResponse</tt>. The implementation directly
 | 
				
			||||||
 | 
					 * correlates with the class 0 function <i>write coil (FC 5)</i>. It
 | 
				
			||||||
 | 
					 * encapsulates the corresponding response message.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class WriteCoilResponse extends ModbusResponse {
 | 
				
			||||||
 | 
					    private boolean coil = false;
 | 
				
			||||||
 | 
					    private int reference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>WriteCoilResponse</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public WriteCoilResponse() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.WRITE_COIL);
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>WriteCoilResponse</tt> instance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param reference the offset were writing was started from.
 | 
				
			||||||
 | 
					     * @param b         the state of the coil; true set, false reset.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public WriteCoilResponse(int reference, boolean b) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.WRITE_COIL);
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setReference(reference);
 | 
				
			||||||
 | 
					        setCoil(b);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Gets the state that has been returned in this <tt>WriteCoilRequest</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return true if the coil is set, false if unset.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean getCoil() {
 | 
				
			||||||
 | 
					        return coil;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the state that has been returned in the raw response.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param b true if the coil should be set of false if it should be unset.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setCoil(boolean b) {
 | 
				
			||||||
 | 
					        coil = b;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the reference of the register of the coil that has been written
 | 
				
			||||||
 | 
					     * to with the request.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the reference of the coil's register.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getReference() {
 | 
				
			||||||
 | 
					        return reference;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the reference of the register of the coil that has been written to
 | 
				
			||||||
 | 
					     * with the request.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref the reference of the coil's register.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setReference(int ref) {
 | 
				
			||||||
 | 
					        reference = ref;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.write(getMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        byte data[] = new byte[4];
 | 
				
			||||||
 | 
					        din.readFully(data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setReference(((data[0] << 8) | (data[1] & 0xff)));
 | 
				
			||||||
 | 
					        setCoil(data[2] == Modbus.COIL_ON);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte result[] = new byte[4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result[0] = (byte)((reference >> 8) & 0xff);
 | 
				
			||||||
 | 
					        result[1] = (byte)(reference & 0xff);
 | 
				
			||||||
 | 
					        if (coil) {
 | 
				
			||||||
 | 
					            result[2] = Modbus.COIL_ON_BYTES[0];
 | 
				
			||||||
 | 
					            result[3] = Modbus.COIL_ON_BYTES[1];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            result[2] = Modbus.COIL_OFF_BYTES[0];
 | 
				
			||||||
 | 
					            result[3] = Modbus.COIL_OFF_BYTES[1];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,304 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.WriteFileRecordResponse.RecordResponse;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractModbusListener;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>Write File Record</tt> request.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Julie Haugh (jfh@ghgande.com)
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class WriteFileRecordRequest extends ModbusRequest {
 | 
				
			||||||
 | 
					    private RecordRequest[] records;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>Write File Record</tt> request
 | 
				
			||||||
 | 
					     * instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public WriteFileRecordRequest() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.WRITE_FILE_RECORD);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Set up space for the initial header.
 | 
				
			||||||
 | 
					        setDataLength(1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getRequestSize -- return the total request size.  This is useful
 | 
				
			||||||
 | 
					     * for determining if a new record can be added.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return size in bytes of response.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getRequestSize() {
 | 
				
			||||||
 | 
					        if (records == null) {
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int size = 1;
 | 
				
			||||||
 | 
					        for (RecordRequest record : records) {
 | 
				
			||||||
 | 
					            size += record.getRequestSize();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return size;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getRequestCount -- return the number of record requests in this
 | 
				
			||||||
 | 
					     * message.
 | 
				
			||||||
 | 
					     * @return number of record requests in this message
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getRequestCount() {
 | 
				
			||||||
 | 
					        if (records == null) {
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return records.length;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getRecord -- return the record request indicated by the reference
 | 
				
			||||||
 | 
					     * @param reference Register reference
 | 
				
			||||||
 | 
					     * @return the record request indicated by the reference
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public RecordRequest getRecord(int reference) {
 | 
				
			||||||
 | 
					        return records[reference];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * addRequest -- add a new record request.
 | 
				
			||||||
 | 
					     * @param request Request record
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void addRequest(RecordRequest request) {
 | 
				
			||||||
 | 
					        if (request.getRequestSize() + getRequestSize() > 248) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (records == null) {
 | 
				
			||||||
 | 
					            records = new RecordRequest[1];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            RecordRequest old[] = records;
 | 
				
			||||||
 | 
					            records = new RecordRequest[old.length + 1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            System.arraycopy(old, 0, records, 0, old.length);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        records[records.length - 1] = request;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setDataLength(getRequestSize());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse getResponse() {
 | 
				
			||||||
 | 
					        return updateResponseWithHeader(new WriteFileRecordResponse());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse createResponse(AbstractModbusListener listener) {
 | 
				
			||||||
 | 
					        WriteFileRecordResponse response = (WriteFileRecordResponse)getResponse();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Get the process image.
 | 
				
			||||||
 | 
					        ProcessImage procimg = listener.getProcessImage(getUnitID());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // There is a list of requests to be resolved.
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            for (int i = 0; i < getRequestCount(); i++) {
 | 
				
			||||||
 | 
					                RecordRequest recordRequest = getRecord(i);
 | 
				
			||||||
 | 
					                if (recordRequest.getFileNumber() < 0 || recordRequest.getFileNumber() >= procimg.getFileCount()) {
 | 
				
			||||||
 | 
					                    return createExceptionResponse(Modbus.ILLEGAL_ADDRESS_EXCEPTION);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                File file = procimg.getFileByNumber(recordRequest.getFileNumber());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (recordRequest.getRecordNumber() < 0 || recordRequest.getRecordNumber() >= file.getRecordCount()) {
 | 
				
			||||||
 | 
					                    return createExceptionResponse(Modbus.ILLEGAL_ADDRESS_EXCEPTION);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                Record record = file.getRecord(recordRequest.getRecordNumber());
 | 
				
			||||||
 | 
					                int registers = recordRequest.getWordCount();
 | 
				
			||||||
 | 
					                if (record == null && registers != 0) {
 | 
				
			||||||
 | 
					                    return createExceptionResponse(Modbus.ILLEGAL_ADDRESS_EXCEPTION);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                short data[] = new short[registers];
 | 
				
			||||||
 | 
					                for (int j = 0; j < registers; j++) {
 | 
				
			||||||
 | 
					                    Register register = record.getRegister(j);
 | 
				
			||||||
 | 
					                    if (register == null) {
 | 
				
			||||||
 | 
					                        return createExceptionResponse(Modbus.ILLEGAL_ADDRESS_EXCEPTION);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    register.setValue(recordRequest.getRegister(j).getValue());
 | 
				
			||||||
 | 
					                    data[j] = recordRequest.getRegister(j).toShort();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                RecordResponse recordResponse = new RecordResponse(file.getFileNumber(), record == null ? 0 : record.getRecordNumber(), data);
 | 
				
			||||||
 | 
					                response.addResponse(recordResponse);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (IllegalAddressException e) {
 | 
				
			||||||
 | 
					            return createExceptionResponse(Modbus.ILLEGAL_ADDRESS_EXCEPTION);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * writeData -- output this Modbus message to dout.
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be written
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.write(getMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * readData -- convert the byte stream into a request.
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be read
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        int byteCount = din.readUnsignedByte();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        records = new RecordRequest[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (int offset = 1; offset + 7 < byteCount; ) {
 | 
				
			||||||
 | 
					            int function = din.readUnsignedByte();
 | 
				
			||||||
 | 
					            int file = din.readUnsignedShort();
 | 
				
			||||||
 | 
					            int record = din.readUnsignedShort();
 | 
				
			||||||
 | 
					            int count = din.readUnsignedShort();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            offset += 7;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (function != 6) {
 | 
				
			||||||
 | 
					                throw new IOException();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (record < 0 || record >= 10000) {
 | 
				
			||||||
 | 
					                throw new IOException();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (count < 0 || count >= 126) {
 | 
				
			||||||
 | 
					                throw new IOException();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            short registers[] = new short[count];
 | 
				
			||||||
 | 
					            for (int j = 0; j < count; j++) {
 | 
				
			||||||
 | 
					                registers[j] = din.readShort();
 | 
				
			||||||
 | 
					                offset += 2;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            RecordRequest dummy[] = new RecordRequest[records.length + 1];
 | 
				
			||||||
 | 
					            if (records.length > 0) {
 | 
				
			||||||
 | 
					                System.arraycopy(records, 0, dummy, 0, records.length);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            records = dummy;
 | 
				
			||||||
 | 
					            records[records.length - 1] = new RecordRequest(file, record, registers);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getMessage -- return the raw binary message.
 | 
				
			||||||
 | 
					     * @return the raw binary message
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte results[] = new byte[getRequestSize()];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        results[0] = (byte)(getRequestSize() - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int offset = 1;
 | 
				
			||||||
 | 
					        for (RecordRequest record : records) {
 | 
				
			||||||
 | 
					            record.getRequest(results, offset);
 | 
				
			||||||
 | 
					            offset += record.getRequestSize();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return results;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static class RecordRequest {
 | 
				
			||||||
 | 
					        private int fileNumber;
 | 
				
			||||||
 | 
					        private int recordNumber;
 | 
				
			||||||
 | 
					        private int wordCount;
 | 
				
			||||||
 | 
					        private byte data[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public RecordRequest(int file, int record, short[] values) {
 | 
				
			||||||
 | 
					            fileNumber = file;
 | 
				
			||||||
 | 
					            recordNumber = record;
 | 
				
			||||||
 | 
					            wordCount = values.length;
 | 
				
			||||||
 | 
					            data = new byte[wordCount * 2];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            int offset = 0;
 | 
				
			||||||
 | 
					            for (int i = 0; i < wordCount; i++) {
 | 
				
			||||||
 | 
					                data[offset++] = (byte)(values[i] >> 8);
 | 
				
			||||||
 | 
					                data[offset++] = (byte)(values[i] & 0xFF);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public int getFileNumber() {
 | 
				
			||||||
 | 
					            return fileNumber;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public int getRecordNumber() {
 | 
				
			||||||
 | 
					            return recordNumber;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public int getWordCount() {
 | 
				
			||||||
 | 
					            return wordCount;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public SimpleRegister getRegister(int register) {
 | 
				
			||||||
 | 
					            if (register < 0 || register >= wordCount) {
 | 
				
			||||||
 | 
					                throw new IllegalAddressException("0 <= " + register + " < " + wordCount);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            byte b1 = data[register * 2];
 | 
				
			||||||
 | 
					            byte b2 = data[register * 2 + 1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return new SimpleRegister(b1, b2);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * getRequestSize -- return the size of the response in bytes.
 | 
				
			||||||
 | 
					         * @return the size of the response in bytes
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        public int getRequestSize() {
 | 
				
			||||||
 | 
					            return 7 + wordCount * 2;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void getRequest(byte[] request, int offset) {
 | 
				
			||||||
 | 
					            request[offset++] = 6;
 | 
				
			||||||
 | 
					            request[offset++] = (byte)(fileNumber >> 8);
 | 
				
			||||||
 | 
					            request[offset++] = (byte)(fileNumber & 0xFF);
 | 
				
			||||||
 | 
					            request[offset++] = (byte)(recordNumber >> 8);
 | 
				
			||||||
 | 
					            request[offset++] = (byte)(recordNumber & 0xFF);
 | 
				
			||||||
 | 
					            request[offset++] = (byte)(wordCount >> 8);
 | 
				
			||||||
 | 
					            request[offset++] = (byte)(wordCount & 0xFF);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            System.arraycopy(data, 0, request, offset, data.length);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public byte[] getRequest() {
 | 
				
			||||||
 | 
					            byte[] request = new byte[7 + 2 * wordCount];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            getRequest(request, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return request;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,235 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.SimpleRegister;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>WriteFileRecordResponse</tt>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Julie
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class WriteFileRecordResponse extends ModbusResponse {
 | 
				
			||||||
 | 
					    private RecordResponse[] records;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>WriteFileRecordResponse</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public WriteFileRecordResponse() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.WRITE_FILE_RECORD);
 | 
				
			||||||
 | 
					        setDataLength(7);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getRequestSize -- return the total request size.  This is useful
 | 
				
			||||||
 | 
					     * for determining if a new record can be added.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return size in bytes of response.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getResponseSize() {
 | 
				
			||||||
 | 
					        if (records == null) {
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int size = 1;
 | 
				
			||||||
 | 
					        for (RecordResponse record : records) {
 | 
				
			||||||
 | 
					            size += record.getResponseSize();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return size;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getRequestCount -- return the number of record requests in this
 | 
				
			||||||
 | 
					     * message.
 | 
				
			||||||
 | 
					     * @return the number of record requests in this message
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getRequestCount() {
 | 
				
			||||||
 | 
					        if (records == null) {
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return records.length;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getRecord -- return the record request indicated by the reference
 | 
				
			||||||
 | 
					     * @param index Record to get
 | 
				
			||||||
 | 
					     * @return the record request indicated by the reference
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public RecordResponse getRecord(int index) {
 | 
				
			||||||
 | 
					        return records[index];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * addResponse -- add a new record response.
 | 
				
			||||||
 | 
					     * @param response Add record response
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void addResponse(RecordResponse response) {
 | 
				
			||||||
 | 
					        if (response.getResponseSize() + getResponseSize() > 248) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (records == null) {
 | 
				
			||||||
 | 
					            records = new RecordResponse[1];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            RecordResponse old[] = records;
 | 
				
			||||||
 | 
					            records = new RecordResponse[old.length + 1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            System.arraycopy(old, 0, records, 0, old.length);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        records[records.length - 1] = response;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setDataLength(getResponseSize());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.write(getMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        int byteCount = din.readUnsignedByte();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        records = new RecordResponse[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (int offset = 1; offset + 7 < byteCount; ) {
 | 
				
			||||||
 | 
					            int function = din.readUnsignedByte();
 | 
				
			||||||
 | 
					            int file = din.readUnsignedShort();
 | 
				
			||||||
 | 
					            int record = din.readUnsignedShort();
 | 
				
			||||||
 | 
					            int count = din.readUnsignedShort();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            offset += 7;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (function != 6) {
 | 
				
			||||||
 | 
					                throw new IOException();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (record < 0 || record >= 10000) {
 | 
				
			||||||
 | 
					                throw new IOException();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (count < 0 || count >= 126) {
 | 
				
			||||||
 | 
					                throw new IOException();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            short registers[] = new short[count];
 | 
				
			||||||
 | 
					            for (int j = 0; j < count; j++) {
 | 
				
			||||||
 | 
					                registers[j] = din.readShort();
 | 
				
			||||||
 | 
					                offset += 2;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            RecordResponse dummy[] = new RecordResponse[records.length + 1];
 | 
				
			||||||
 | 
					            if (records.length > 0) {
 | 
				
			||||||
 | 
					                System.arraycopy(records, 0, dummy, 0, records.length);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            records = dummy;
 | 
				
			||||||
 | 
					            records[records.length - 1] = new RecordResponse(file, record, registers);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte results[] = new byte[getResponseSize()];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        results[0] = (byte)(getResponseSize() - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int offset = 1;
 | 
				
			||||||
 | 
					        for (RecordResponse record : records) {
 | 
				
			||||||
 | 
					            record.getResponse(results, offset);
 | 
				
			||||||
 | 
					            offset += record.getResponseSize();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return results;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static class RecordResponse {
 | 
				
			||||||
 | 
					        private int fileNumber;
 | 
				
			||||||
 | 
					        private int recordNumber;
 | 
				
			||||||
 | 
					        private int wordCount;
 | 
				
			||||||
 | 
					        private byte data[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public RecordResponse(int file, int record, short[] values) {
 | 
				
			||||||
 | 
					            fileNumber = file;
 | 
				
			||||||
 | 
					            recordNumber = record;
 | 
				
			||||||
 | 
					            wordCount = values.length;
 | 
				
			||||||
 | 
					            data = new byte[wordCount * 2];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            int offset = 0;
 | 
				
			||||||
 | 
					            for (int i = 0; i < wordCount; i++) {
 | 
				
			||||||
 | 
					                data[offset++] = (byte)(values[i] >> 8);
 | 
				
			||||||
 | 
					                data[offset++] = (byte)(values[i] & 0xFF);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public int getFileNumber() {
 | 
				
			||||||
 | 
					            return fileNumber;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public int getRecordNumber() {
 | 
				
			||||||
 | 
					            return recordNumber;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public int getWordCount() {
 | 
				
			||||||
 | 
					            return wordCount;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public SimpleRegister getRegister(int register) {
 | 
				
			||||||
 | 
					            if (register < 0 || register >= wordCount) {
 | 
				
			||||||
 | 
					                throw new IndexOutOfBoundsException("0 <= " + register + " < " + wordCount);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            byte b1 = data[register * 2];
 | 
				
			||||||
 | 
					            byte b2 = data[register * 2 + 1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return new SimpleRegister(b1, b2);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * getResponseSize -- return the size of the response in bytes.
 | 
				
			||||||
 | 
					         * @return the size of the response in bytes
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        public int getResponseSize() {
 | 
				
			||||||
 | 
					            return 7 + wordCount * 2;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void getResponse(byte[] response, int offset) {
 | 
				
			||||||
 | 
					            response[offset++] = 6;
 | 
				
			||||||
 | 
					            response[offset++] = (byte)(fileNumber >> 8);
 | 
				
			||||||
 | 
					            response[offset++] = (byte)(fileNumber & 0xFF);
 | 
				
			||||||
 | 
					            response[offset++] = (byte)(recordNumber >> 8);
 | 
				
			||||||
 | 
					            response[offset++] = (byte)(recordNumber & 0xFF);
 | 
				
			||||||
 | 
					            response[offset++] = (byte)(wordCount >> 8);
 | 
				
			||||||
 | 
					            response[offset++] = (byte)(wordCount & 0xFF);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            System.arraycopy(data, 0, response, offset, data.length);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public byte[] getResponse() {
 | 
				
			||||||
 | 
					            byte[] response = new byte[7 + 2 * wordCount];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            getResponse(response, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return response;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,261 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractModbusListener;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.DigitalOut;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.IllegalAddressException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.ProcessImage;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.util.BitVector;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>WriteMultipleCoilsRequest</tt>. The implementation
 | 
				
			||||||
 | 
					 * directly correlates with the class 1 function <i>write multiple coils (FC
 | 
				
			||||||
 | 
					 * 15)</i>. It encapsulates the corresponding request message.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * Coils are understood as bits that can be manipulated (i.e. set or cleared).
 | 
				
			||||||
 | 
					 * </p>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Julie Haugh
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class WriteMultipleCoilsRequest extends ModbusRequest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // instance attributes
 | 
				
			||||||
 | 
					    private int reference;
 | 
				
			||||||
 | 
					    private BitVector coils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>WriteMultipleCoilsRequest</tt> instance with the
 | 
				
			||||||
 | 
					     * given reference and coil values.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref the index of the first coil to be written.
 | 
				
			||||||
 | 
					     * @param bv  the coil values to be written.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public WriteMultipleCoilsRequest(int ref, BitVector bv) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.WRITE_MULTIPLE_COILS);
 | 
				
			||||||
 | 
					        setDataLength(bv.byteSize() + 5);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setReference(ref);
 | 
				
			||||||
 | 
					        coils = bv;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>WriteMultipleCoilsRequest</tt> instance with a given
 | 
				
			||||||
 | 
					     * reference and count of coils to be written, followed by the actual byte
 | 
				
			||||||
 | 
					     * count, and then <i>count</i> number of bytes.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref   the index of the first coil to be written.
 | 
				
			||||||
 | 
					     * @param count the number of coils to be written.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public WriteMultipleCoilsRequest(int ref, int count) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.WRITE_MULTIPLE_COILS);
 | 
				
			||||||
 | 
					        setDataLength((count + 7) / 8 + 5);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setReference(ref);
 | 
				
			||||||
 | 
					        coils = new BitVector(count);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>WriteMultipleCoilsRequest</tt> instance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * A minimal message contains the reference to the first coil as a
 | 
				
			||||||
 | 
					     * <tt>short</tt>, the number of coils as a <tt>short</tt>, and not less
 | 
				
			||||||
 | 
					     * than one <tt>byte</tt> of coil data.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public WriteMultipleCoilsRequest() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.WRITE_MULTIPLE_COILS);
 | 
				
			||||||
 | 
					        setDataLength(5);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        coils = new BitVector(1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse getResponse() {
 | 
				
			||||||
 | 
					        return updateResponseWithHeader(new WriteMultipleCoilsResponse());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse createResponse(AbstractModbusListener listener) {
 | 
				
			||||||
 | 
					        WriteMultipleCoilsResponse response;
 | 
				
			||||||
 | 
					        DigitalOut douts[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 1. get process image
 | 
				
			||||||
 | 
					        ProcessImage procimg = listener.getProcessImage(getUnitID());
 | 
				
			||||||
 | 
					        // 2. get coil range
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            douts = procimg.getDigitalOutRange(reference, coils.size());
 | 
				
			||||||
 | 
					            // 3. set coils
 | 
				
			||||||
 | 
					            for (int i = 0; i < douts.length; i++) {
 | 
				
			||||||
 | 
					                douts[i].set(coils.getBit(i));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (IllegalAddressException iaex) {
 | 
				
			||||||
 | 
					            return createExceptionResponse(Modbus.ILLEGAL_ADDRESS_EXCEPTION);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        response = (WriteMultipleCoilsResponse)getResponse();
 | 
				
			||||||
 | 
					        response.setBitCount(coils.size());
 | 
				
			||||||
 | 
					        response.setReference(reference);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getReference - Returns the reference of the coil to to start writing to
 | 
				
			||||||
 | 
					     * with this <tt>WriteMultipleCoilsRequest</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the reference of the coil to start writing to as an <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getReference() {
 | 
				
			||||||
 | 
					        return reference;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setReference - Sets the reference of the coil to start writing to with
 | 
				
			||||||
 | 
					     * this <tt>WriteMultipleCoilsRequest</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref the reference of the coil to start writing to.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setReference(int ref) {
 | 
				
			||||||
 | 
					        reference = ref;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getBitCount - Returns the number of coils written with the request.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of coils that have been written.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getBitCount() {
 | 
				
			||||||
 | 
					        if (coils == null) {
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            return coils.size();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getByteCount - Returns the number of bytes required for packing the
 | 
				
			||||||
 | 
					     * coils.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of bytes required for packing the coils.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getByteCount() {
 | 
				
			||||||
 | 
					        return coils.byteSize();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getCoilStatus - Returns the status of the specified coil.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param index the index of the coil to be tested.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return true if set, false otherwise.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IndexOutOfBoundsException if the given index is out of bounds.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean getCoilStatus(int index) throws IndexOutOfBoundsException {
 | 
				
			||||||
 | 
					        return coils.getBit(index);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setCoilStatus - Sets the status of the specified coil.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param index the index of the coil to be set/reset.
 | 
				
			||||||
 | 
					     * @param b     true if to be set, false for reset.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IndexOutOfBoundsException if the given index is out of bounds.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setCoilStatus(int index, boolean b) throws IndexOutOfBoundsException {
 | 
				
			||||||
 | 
					        coils.setBit(index, b);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getCoils - Returns the <tt>BitVector</tt> instance holding coil status
 | 
				
			||||||
 | 
					     * information.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the coils status as a <tt>BitVector</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public BitVector getCoils() {
 | 
				
			||||||
 | 
					        return coils;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setCoils - Sets the <tt>BitVector</tt> instance holding coil status
 | 
				
			||||||
 | 
					     * information.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param bv a <tt>BitVector</tt> instance holding coil status info.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setCoils(BitVector bv) {
 | 
				
			||||||
 | 
					        coils = bv;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.writeShort(reference);
 | 
				
			||||||
 | 
					        dout.writeShort(coils.size());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dout.writeByte(coils.byteSize());
 | 
				
			||||||
 | 
					        dout.write(coils.getBytes());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        reference = din.readUnsignedShort();
 | 
				
			||||||
 | 
					        int bitcount = din.readUnsignedShort();
 | 
				
			||||||
 | 
					        int coilBytes = din.readUnsignedByte();
 | 
				
			||||||
 | 
					        byte[] data = new byte[coilBytes];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (int k = 0; k < coilBytes; k++) {
 | 
				
			||||||
 | 
					            data[k] = din.readByte();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // decode bytes into BitVector, sets data and bitcount
 | 
				
			||||||
 | 
					        coils = BitVector.createBitVector(data, bitcount);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // update data length
 | 
				
			||||||
 | 
					        setDataLength(coilBytes + 5);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        int len = coils.byteSize() + 5;
 | 
				
			||||||
 | 
					        byte result[] = new byte[len];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result[0] = (byte)((reference >> 8) & 0xff);
 | 
				
			||||||
 | 
					        result[1] = (byte)(reference & 0xff);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result[2] = (byte)((coils.size() >> 8) & 0xff);
 | 
				
			||||||
 | 
					        result[3] = (byte)(coils.size() & 0xff);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result[4] = (byte)coils.byteSize();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        System.arraycopy(coils.getBytes(), 0, result, 5, coils.byteSize());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,135 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>WriteMultipleCoilsResponse</tt>. The implementation
 | 
				
			||||||
 | 
					 * directly correlates with the class 1 function <i>write multiple coils (FC
 | 
				
			||||||
 | 
					 * 15)</i>. It encapsulates the corresponding response message.
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * Coils are understood as bits that can be manipulated (i.e. set or cleared).
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Julie Haugh
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class WriteMultipleCoilsResponse extends ModbusResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // instance attributes
 | 
				
			||||||
 | 
					    private int reference;
 | 
				
			||||||
 | 
					    private int bitCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>WriteMultipleCoilsResponse</tt> instance with a
 | 
				
			||||||
 | 
					     * given count of coils and starting reference.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref   the offset to begin writing from.
 | 
				
			||||||
 | 
					     * @param count the number of coils to be written.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public WriteMultipleCoilsResponse(int ref, int count) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					        reference = ref;
 | 
				
			||||||
 | 
					        bitCount = count;
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>WriteMultipleCoilsResponse</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public WriteMultipleCoilsResponse() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getReference - Returns the reference of the coil to start reading from
 | 
				
			||||||
 | 
					     * with this <tt>WriteMultipleCoilsResponse</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the reference of the coil to start reading from as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getReference() {
 | 
				
			||||||
 | 
					        return reference;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setReference - Sets the reference to the coil that is the first coil in
 | 
				
			||||||
 | 
					     * this response.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref Rgister address of coil
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setReference(int ref) {
 | 
				
			||||||
 | 
					        reference = ref;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getBitCount - Returns the quantity of coils written with the request.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the quantity of coils that have been written.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getBitCount() {
 | 
				
			||||||
 | 
					        return bitCount;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setBitCount - Sets the number of coils that will be in a response.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param count the number of coils in the response.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setBitCount(int count) {
 | 
				
			||||||
 | 
					        bitCount = count;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * writeData - Copy the attribute values for this message to the output
 | 
				
			||||||
 | 
					     * buffer.
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be written
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dout.writeShort(reference);
 | 
				
			||||||
 | 
					        dout.writeShort(bitCount);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * readData - Initialize the attribute values for this message from the
 | 
				
			||||||
 | 
					     * input buffer.
 | 
				
			||||||
 | 
					     * @throws IOException If the data cannot be read
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        reference = din.readUnsignedShort();
 | 
				
			||||||
 | 
					        bitCount = din.readUnsignedShort();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte results[] = new byte[4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        results[0] = (byte)((reference >> 8) & 0xff);
 | 
				
			||||||
 | 
					        results[1] = (byte)(reference & 0xff);
 | 
				
			||||||
 | 
					        results[2] = (byte)((bitCount >> 8) & 0xff);
 | 
				
			||||||
 | 
					        results[3] = (byte)(bitCount & 0xff);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return results;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,322 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.io.NonWordDataHandler;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractModbusListener;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.IllegalAddressException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.ProcessImage;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.Register;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.SimpleRegister;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.util.Arrays;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>WriteMultipleRegistersRequest</tt>. The
 | 
				
			||||||
 | 
					 * implementation directly correlates with the class 0 function <i>write
 | 
				
			||||||
 | 
					 * multiple registers (FC 16)</i>. It encapsulates the corresponding request
 | 
				
			||||||
 | 
					 * message.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author jfhaugh
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class WriteMultipleRegistersRequest extends ModbusRequest {
 | 
				
			||||||
 | 
					    private int reference;
 | 
				
			||||||
 | 
					    private Register[] registers;
 | 
				
			||||||
 | 
					    private NonWordDataHandler nonWordDataHandler = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>WriteMultipleRegistersRequest</tt> instance with a
 | 
				
			||||||
 | 
					     * given starting reference and values to be written.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param first     -- the address of the first register to write to.
 | 
				
			||||||
 | 
					     * @param registers -- the registers to be written.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public WriteMultipleRegistersRequest(int first, Register[] registers) {
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.WRITE_MULTIPLE_REGISTERS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setReference(first);
 | 
				
			||||||
 | 
					        setRegisters(registers);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>WriteMultipleRegistersRequest</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public WriteMultipleRegistersRequest() {
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.WRITE_MULTIPLE_REGISTERS);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse getResponse() {
 | 
				
			||||||
 | 
					        return updateResponseWithHeader(new WriteMultipleRegistersResponse());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * createResponse - Returns the <tt>WriteMultipleRegistersResponse</tt> that
 | 
				
			||||||
 | 
					     * represents the answer to this <tt>WriteMultipleRegistersRequest</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * The implementation should take care about assembling the reply to this
 | 
				
			||||||
 | 
					     * <tt>WriteMultipleRegistersRequest</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * This method is used to create responses from the process image associated
 | 
				
			||||||
 | 
					     * with the listener. It is commonly used to implement Modbus
 | 
				
			||||||
 | 
					     * slave instances.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the corresponding ModbusResponse.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * createResponse() must be able to handle the case where the word
 | 
				
			||||||
 | 
					     * data that is in the response is actually non-word data. That is,
 | 
				
			||||||
 | 
					     * where the slave device has data which are not actually
 | 
				
			||||||
 | 
					     * <tt>short</tt> values in the range of registers being processed.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse createResponse(AbstractModbusListener listener) {
 | 
				
			||||||
 | 
					        WriteMultipleRegistersResponse response;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (nonWordDataHandler == null) {
 | 
				
			||||||
 | 
					            Register[] regs;
 | 
				
			||||||
 | 
					            // 1. get process image
 | 
				
			||||||
 | 
					            ProcessImage procimg = listener.getProcessImage(getUnitID());
 | 
				
			||||||
 | 
					            // 2. get registers
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                regs = procimg.getRegisterRange(getReference(), getWordCount());
 | 
				
			||||||
 | 
					                // 3. set Register values
 | 
				
			||||||
 | 
					                for (int i = 0; i < regs.length; i++) {
 | 
				
			||||||
 | 
					                    regs[i].setValue(this.getRegister(i).getValue());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (IllegalAddressException iaex) {
 | 
				
			||||||
 | 
					                return createExceptionResponse(Modbus.ILLEGAL_ADDRESS_EXCEPTION);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            response = (WriteMultipleRegistersResponse)getResponse();
 | 
				
			||||||
 | 
					            response.setReference(getReference());
 | 
				
			||||||
 | 
					            response.setWordCount(getWordCount());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            int result = nonWordDataHandler.commitUpdate();
 | 
				
			||||||
 | 
					            if (result > 0) {
 | 
				
			||||||
 | 
					                return createExceptionResponse(result);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            response = (WriteMultipleRegistersResponse)getResponse();
 | 
				
			||||||
 | 
					            response.setReference(getReference());
 | 
				
			||||||
 | 
					            response.setWordCount(nonWordDataHandler.getWordCount());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return response;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setReference - Returns the reference of the register to start writing to
 | 
				
			||||||
 | 
					     * with this <tt>WriteMultipleRegistersRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the reference of the register to start writing to as <tt>int</tt>
 | 
				
			||||||
 | 
					     * .
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getReference() {
 | 
				
			||||||
 | 
					        return reference;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setReference - Sets the reference of the register to write to with this
 | 
				
			||||||
 | 
					     * <tt>WriteMultipleRegistersRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref the reference of the register to start writing to as an
 | 
				
			||||||
 | 
					     *            <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setReference(int ref) {
 | 
				
			||||||
 | 
					        reference = ref;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getRegisters - Returns the registers to be written with this
 | 
				
			||||||
 | 
					     * <tt>WriteMultipleRegistersRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the registers to be written as <tt>Register[]</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized Register[] getRegisters() {
 | 
				
			||||||
 | 
					        Register[] dest = new Register[registers.length];
 | 
				
			||||||
 | 
					        System.arraycopy(registers, 0, dest, 0, dest.length);
 | 
				
			||||||
 | 
					        return dest;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setRegisters - Sets the registers to be written with this
 | 
				
			||||||
 | 
					     * <tt>WriteMultipleRegistersRequest</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param registers the registers to be written as <tt>Register[]</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setRegisters(Register[] registers) {
 | 
				
			||||||
 | 
					        this.registers = registers == null ? null : Arrays.copyOf(registers, registers.length);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getRegister - Returns the <tt>Register</tt> at the given position.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param index the relative index of the <tt>Register</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the register as <tt>Register</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IndexOutOfBoundsException if the index is out of bounds.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public Register getRegister(int index) throws IndexOutOfBoundsException {
 | 
				
			||||||
 | 
					        if (index < 0) {
 | 
				
			||||||
 | 
					            throw new IndexOutOfBoundsException(index + " < 0");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (index >= getWordCount()) {
 | 
				
			||||||
 | 
					            throw new IndexOutOfBoundsException(index + " > " + getWordCount());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return registers[index];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getRegisterValue - Returns the value of the specified register.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param index the index of the desired register.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the value as an <tt>int</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IndexOutOfBoundsException if the index is out of bounds.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getRegisterValue(int index) throws IndexOutOfBoundsException {
 | 
				
			||||||
 | 
					        return getRegister(index).toUnsignedShort();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getByteCount - Returns the number of bytes representing the values to be
 | 
				
			||||||
 | 
					     * written.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of bytes to be written as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getByteCount() {
 | 
				
			||||||
 | 
					        return getWordCount() * 2;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getWordCount - Returns the number of words to be written.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of words to be written as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getWordCount() {
 | 
				
			||||||
 | 
					        if (registers == null) {
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return registers.length;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * getNonWordDataHandler - Returns the actual non word data handler.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the actual <tt>NonWordDataHandler</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public NonWordDataHandler getNonWordDataHandler() {
 | 
				
			||||||
 | 
					        return nonWordDataHandler;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * setNonWordHandler - Sets a non word data handler. A non-word data handler
 | 
				
			||||||
 | 
					     * is responsible for converting words from a Modbus packet into the
 | 
				
			||||||
 | 
					     * non-word values associated with the actual device's registers.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param dhandler a <tt>NonWordDataHandler</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setNonWordDataHandler(NonWordDataHandler dhandler) {
 | 
				
			||||||
 | 
					        nonWordDataHandler = dhandler;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeData(DataOutput output) throws IOException {
 | 
				
			||||||
 | 
					        output.write(getMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readData(DataInput input) throws IOException {
 | 
				
			||||||
 | 
					        reference = input.readUnsignedShort();
 | 
				
			||||||
 | 
					        int registerCount = input.readUnsignedShort();
 | 
				
			||||||
 | 
					        int byteCount = input.readUnsignedByte();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (nonWordDataHandler == null) {
 | 
				
			||||||
 | 
					            byte buffer[] = new byte[byteCount];
 | 
				
			||||||
 | 
					            input.readFully(buffer, 0, byteCount);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            int offset = 0;
 | 
				
			||||||
 | 
					            registers = new Register[registerCount];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (int register = 0; register < registerCount; register++) {
 | 
				
			||||||
 | 
					                registers[register] = new SimpleRegister(buffer[offset], buffer[offset + 1]);
 | 
				
			||||||
 | 
					                offset += 2;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            nonWordDataHandler.readData(input, reference, registerCount);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        int len = 5;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (registers != null) {
 | 
				
			||||||
 | 
					            len += registers.length * 2;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        byte result[] = new byte[len];
 | 
				
			||||||
 | 
					        int registerCount = registers != null ? registers.length : 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result[0] = (byte)((reference >> 8) & 0xff);
 | 
				
			||||||
 | 
					        result[1] = (byte)(reference & 0xff);
 | 
				
			||||||
 | 
					        result[2] = (byte)((registerCount >> 8) & 0xff);
 | 
				
			||||||
 | 
					        result[3] = (byte)(registerCount & 0xff);
 | 
				
			||||||
 | 
					        result[4] = (byte)(registerCount * 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int offset = 5;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (nonWordDataHandler == null) {
 | 
				
			||||||
 | 
					            for (int i = 0; i < registerCount; i++) {
 | 
				
			||||||
 | 
					                byte bytes[] = registers[i].toBytes();
 | 
				
			||||||
 | 
					                result[offset++] = bytes[0];
 | 
				
			||||||
 | 
					                result[offset++] = bytes[1];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            nonWordDataHandler.prepareData(reference, registerCount);
 | 
				
			||||||
 | 
					            byte bytes[] = nonWordDataHandler.getData();
 | 
				
			||||||
 | 
					            if (bytes != null) {
 | 
				
			||||||
 | 
					                int nonWordBytes = bytes.length;
 | 
				
			||||||
 | 
					                if (nonWordBytes > registerCount * 2) {
 | 
				
			||||||
 | 
					                    nonWordBytes = registerCount * 2;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                System.arraycopy(bytes, 0, result, offset, nonWordBytes);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,138 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>WriteMultipleRegistersResponse</tt>. The
 | 
				
			||||||
 | 
					 * implementation directly correlates with the class 0 function <i>read multiple
 | 
				
			||||||
 | 
					 * registers (FC 16)</i>. It encapsulates the corresponding response message.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class WriteMultipleRegistersResponse extends ModbusResponse {
 | 
				
			||||||
 | 
					    // instance attributes
 | 
				
			||||||
 | 
					    private int wordCount;
 | 
				
			||||||
 | 
					    private int reference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>WriteMultipleRegistersResponse</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public WriteMultipleRegistersResponse() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.WRITE_MULTIPLE_REGISTERS);
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>WriteMultipleRegistersResponse</tt> instance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param reference the offset to start reading from.
 | 
				
			||||||
 | 
					     * @param wordCount the number of words (registers) to be read.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public WriteMultipleRegistersResponse(int reference, int wordCount) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.WRITE_MULTIPLE_REGISTERS);
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.reference = reference;
 | 
				
			||||||
 | 
					        this.wordCount = wordCount;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the reference of the register to start writing to with this
 | 
				
			||||||
 | 
					     * <tt>WriteMultipleRegistersResponse</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the reference of the register to start writing to as <tt>int</tt>
 | 
				
			||||||
 | 
					     * .
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getReference() {
 | 
				
			||||||
 | 
					        return reference;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the reference of the register to start writing to with this
 | 
				
			||||||
 | 
					     * <tt>WriteMultipleRegistersResponse</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref the reference of the register to start writing to as
 | 
				
			||||||
 | 
					     *            <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setReference(int ref) {
 | 
				
			||||||
 | 
					        reference = ref;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the number of bytes that have been written.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of bytes that have been read as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getByteCount() {
 | 
				
			||||||
 | 
					        return wordCount * 2;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the number of words that have been read. The returned value
 | 
				
			||||||
 | 
					     * should be half of the byte count of the response.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the number of words that have been read as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getWordCount() {
 | 
				
			||||||
 | 
					        return wordCount;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the number of words that have been returned.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param count the number of words as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setWordCount(int count) {
 | 
				
			||||||
 | 
					        wordCount = count;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.write(getMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        setReference(din.readUnsignedShort());
 | 
				
			||||||
 | 
					        setWordCount(din.readUnsignedShort());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte result[] = new byte[4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result[0] = (byte)((reference >> 8) & 0xff);
 | 
				
			||||||
 | 
					        result[1] = (byte)(reference & 0xff);
 | 
				
			||||||
 | 
					        result[2] = (byte)((wordCount >> 8) & 0xff);
 | 
				
			||||||
 | 
					        result[3] = (byte)(wordCount & 0xff);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,156 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.net.AbstractModbusListener;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.IllegalAddressException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.ProcessImage;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.Register;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.SimpleRegister;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>WriteSingleRegisterRequest</tt>. The implementation
 | 
				
			||||||
 | 
					 * directly correlates with the class 0 function <i>write single register (FC
 | 
				
			||||||
 | 
					 * 6)</i>. It encapsulates the corresponding request message.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class WriteSingleRegisterRequest extends ModbusRequest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // instance attributes
 | 
				
			||||||
 | 
					    private int reference;
 | 
				
			||||||
 | 
					    private Register register;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>WriteSingleRegisterRequest</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public WriteSingleRegisterRequest() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.WRITE_SINGLE_REGISTER);
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>WriteSingleRegisterRequest</tt> instance with a
 | 
				
			||||||
 | 
					     * given reference and value to be written.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref the reference number of the register to read from.
 | 
				
			||||||
 | 
					     * @param reg the register containing the data to be written.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public WriteSingleRegisterRequest(int ref, Register reg) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.WRITE_SINGLE_REGISTER);
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        reference = ref;
 | 
				
			||||||
 | 
					        register = reg;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse getResponse() {
 | 
				
			||||||
 | 
					        return updateResponseWithHeader(new WriteSingleRegisterResponse());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ModbusResponse createResponse(AbstractModbusListener listener) {
 | 
				
			||||||
 | 
					        Register reg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 1. get process image
 | 
				
			||||||
 | 
					        ProcessImage procimg = listener.getProcessImage(getUnitID());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 2. get register
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            reg = procimg.getRegister(reference);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 3. set Register
 | 
				
			||||||
 | 
					            reg.setValue(register.toBytes());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (IllegalAddressException iaex) {
 | 
				
			||||||
 | 
					            return createExceptionResponse(Modbus.ILLEGAL_ADDRESS_EXCEPTION);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return updateResponseWithHeader(new WriteSingleRegisterResponse(this.getReference(), reg.getValue()));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the reference of the register to be written to with this
 | 
				
			||||||
 | 
					     * <tt>WriteSingleRegisterRequest</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the reference of the register to be written to.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getReference() {
 | 
				
			||||||
 | 
					        return reference;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the reference of the register to be written to with this
 | 
				
			||||||
 | 
					     * <tt>WriteSingleRegisterRequest</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref the reference of the register to be written to.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setReference(int ref) {
 | 
				
			||||||
 | 
					        reference = ref;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the register to be written with this
 | 
				
			||||||
 | 
					     * <tt>WriteSingleRegisterRequest</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the value to be written to the register.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public Register getRegister() {
 | 
				
			||||||
 | 
					        return register;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the value that should be written to the register with this
 | 
				
			||||||
 | 
					     * <tt>WriteSingleRegisterRequest</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param reg the register to be written.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setRegister(Register reg) {
 | 
				
			||||||
 | 
					        register = reg;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.writeShort(reference);
 | 
				
			||||||
 | 
					        dout.write(register.toBytes());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        reference = din.readUnsignedShort();
 | 
				
			||||||
 | 
					        register = new SimpleRegister(din.readByte(), din.readByte());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte result[] = new byte[4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result[0] = (byte)((reference >> 8) & 0xff);
 | 
				
			||||||
 | 
					        result[1] = (byte)(reference & 0xff);
 | 
				
			||||||
 | 
					        result[2] = (byte)((register.getValue() >> 8) & 0xff);
 | 
				
			||||||
 | 
					        result[3] = (byte)(register.getValue() & 0xff);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,133 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.msg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.DataInput;
 | 
				
			||||||
 | 
					import java.io.DataOutput;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>WriteSingleRegisterResponse</tt>.
 | 
				
			||||||
 | 
					 * The implementation directly correlates with the class 0
 | 
				
			||||||
 | 
					 * function <i>write single register (FC 6)</i>. It
 | 
				
			||||||
 | 
					 * encapsulates the corresponding response message.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public final class WriteSingleRegisterResponse
 | 
				
			||||||
 | 
					        extends ModbusResponse {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //instance attributes
 | 
				
			||||||
 | 
					    private int reference;
 | 
				
			||||||
 | 
					    private int registerValue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>WriteSingleRegisterResponse</tt>
 | 
				
			||||||
 | 
					     * instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public WriteSingleRegisterResponse() {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.WRITE_SINGLE_REGISTER);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>WriteSingleRegisterResponse</tt>
 | 
				
			||||||
 | 
					     * instance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param reference the offset of the register written.
 | 
				
			||||||
 | 
					     * @param value     the value of the register.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public WriteSingleRegisterResponse(int reference, int value) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					        setReference(reference);
 | 
				
			||||||
 | 
					        setRegisterValue(value);
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					        setFunctionCode(Modbus.WRITE_SINGLE_REGISTER);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the value that has been returned in
 | 
				
			||||||
 | 
					     * this <tt>WriteSingleRegisterResponse</tt>.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the value of the register.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getRegisterValue() {
 | 
				
			||||||
 | 
					        return registerValue;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the value that has been returned in the
 | 
				
			||||||
 | 
					     * response message.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param value the returned register value.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void setRegisterValue(int value) {
 | 
				
			||||||
 | 
					        registerValue = value;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the reference of the register
 | 
				
			||||||
 | 
					     * that has been written to.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the reference of the written register.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getReference() {
 | 
				
			||||||
 | 
					        return reference;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the reference of the register that has
 | 
				
			||||||
 | 
					     * been written to.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ref the reference of the written register.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void setReference(int ref) {
 | 
				
			||||||
 | 
					        reference = ref;
 | 
				
			||||||
 | 
					        //setChanged(true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void writeData(DataOutput dout) throws IOException {
 | 
				
			||||||
 | 
					        dout.write(getMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void readData(DataInput din) throws IOException {
 | 
				
			||||||
 | 
					        setReference(din.readUnsignedShort());
 | 
				
			||||||
 | 
					        setRegisterValue(din.readUnsignedShort());
 | 
				
			||||||
 | 
					        //update data length
 | 
				
			||||||
 | 
					        setDataLength(4);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public byte[] getMessage() {
 | 
				
			||||||
 | 
					        byte result[] = new byte[4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result[0] = (byte)((reference >> 8) & 0xff);
 | 
				
			||||||
 | 
					        result[1] = (byte)(reference & 0xff);
 | 
				
			||||||
 | 
					        result[2] = (byte)((registerValue >> 8) & 0xff);
 | 
				
			||||||
 | 
					        result[3] = (byte)(registerValue & 0xff);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,196 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.net;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.ModbusIOException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.io.AbstractModbusTransport;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ModbusRequest;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.msg.ModbusResponse;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.procimg.ProcessImage;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.slave.ModbusSlave;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.slave.ModbusSlaveFactory;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.net.InetAddress;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Definition of a listener class
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public abstract class AbstractModbusListener implements Runnable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final Logger logger = LoggerFactory.getLogger(AbstractModbusListener.class);
 | 
				
			||||||
 | 
					    protected int port = Modbus.DEFAULT_PORT;
 | 
				
			||||||
 | 
					    protected boolean listening;
 | 
				
			||||||
 | 
					    protected InetAddress address;
 | 
				
			||||||
 | 
					    protected String error;
 | 
				
			||||||
 | 
					    protected int timeout = Modbus.DEFAULT_TIMEOUT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Main execution loop for this Modbus interface listener - this is called by
 | 
				
			||||||
 | 
					     * starting the main listening thread
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract void run();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Stop the listener thread for this <tt>ModbusListener</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract void stop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the port to be listened to.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param port the number of the IP port as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setPort(int port) {
 | 
				
			||||||
 | 
					        this.port = ((port > 0) ? port : Modbus.DEFAULT_PORT);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the port being listened on
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Port number > 0
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getPort() {
 | 
				
			||||||
 | 
					        return port;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the address of the interface to be listened to.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param addr an <tt>InetAddress</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setAddress(InetAddress addr) {
 | 
				
			||||||
 | 
					        address = addr;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the address bound to this socket
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Bound address
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public InetAddress getAddress() {
 | 
				
			||||||
 | 
					        return address;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Tests if this <tt>ModbusTCPListener</tt> is listening and accepting
 | 
				
			||||||
 | 
					     * incoming connections.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return true if listening (and accepting incoming connections), false
 | 
				
			||||||
 | 
					     * otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean isListening() {
 | 
				
			||||||
 | 
					        return listening;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Set the listening state of this <tt>ModbusTCPListener</tt> object.
 | 
				
			||||||
 | 
					     * A <tt>ModbusTCPListener</tt> will silently drop any requests if the
 | 
				
			||||||
 | 
					     * listening state is set to <tt>false</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param b listening state
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setListening(boolean b) {
 | 
				
			||||||
 | 
					        listening = b;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns any startup errors that may have aoccurred
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Error string
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public String getError() {
 | 
				
			||||||
 | 
					        return error;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the socket timeout
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Socket timeout in milliseconds
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getTimeout() {
 | 
				
			||||||
 | 
					        return timeout;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the socket timeout
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param timeout Timeout in milliseconds
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setTimeout(int timeout) {
 | 
				
			||||||
 | 
					        this.timeout = timeout;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reads the request, checks it is valid and that the unit ID is ok
 | 
				
			||||||
 | 
					     * and sends back a response
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param transport Transport to read request from
 | 
				
			||||||
 | 
					     * @param listener Listener that the request was received by
 | 
				
			||||||
 | 
					     * @throws ModbusIOException If there is an issue with the transport or transmission
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void handleRequest(AbstractModbusTransport transport, AbstractModbusListener listener) throws ModbusIOException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Get the request from the transport. It will be processed
 | 
				
			||||||
 | 
					        // using an associated process image
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (transport == null) {
 | 
				
			||||||
 | 
					            throw new ModbusIOException("No transport specified");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        ModbusRequest request = transport.readRequest(listener);
 | 
				
			||||||
 | 
					        if (request == null) {
 | 
				
			||||||
 | 
					            throw new ModbusIOException("Request for transport %s is invalid (null)", transport.getClass().getSimpleName());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        ModbusResponse response;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Test if Process image exists for this Unit ID
 | 
				
			||||||
 | 
					        ProcessImage spi = getProcessImage(request.getUnitID());
 | 
				
			||||||
 | 
					        if (spi == null) {
 | 
				
			||||||
 | 
					            response = request.createExceptionResponse(Modbus.ILLEGAL_ADDRESS_EXCEPTION);
 | 
				
			||||||
 | 
					            response.setAuxiliaryType(ModbusResponse.AuxiliaryMessageTypes.UNIT_ID_MISSMATCH);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            response = request.createResponse(this);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (logger.isDebugEnabled()) {
 | 
				
			||||||
 | 
					            logger.debug("Request:{}", request.getHexMessage());
 | 
				
			||||||
 | 
					            logger.debug("Response:{}", response.getHexMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Write the response
 | 
				
			||||||
 | 
					        transport.writeResponse(response);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the related process image for this listener and Unit Id
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param unitId Unit ID
 | 
				
			||||||
 | 
					     * @return Process image associated with this listener and Unit ID
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ProcessImage getProcessImage(int unitId) {
 | 
				
			||||||
 | 
					        ModbusSlave slave = ModbusSlaveFactory.getSlave(this);
 | 
				
			||||||
 | 
					        if (slave != null) {
 | 
				
			||||||
 | 
					            return slave.getProcessImage(unitId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,190 @@
 | 
				
			|||||||
 | 
					package com.ghgande.j2mod.modbus.net;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.io.AbstractModbusTransport;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.util.Set;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Interface that represents a public abstract serial port connection
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Felipe Herranz
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public abstract class AbstractSerialConnection {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Parity values
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static final int NO_PARITY = 0;
 | 
				
			||||||
 | 
					    public static final int ODD_PARITY = 1;
 | 
				
			||||||
 | 
					    public static final int EVEN_PARITY = 2;
 | 
				
			||||||
 | 
					    public static final int MARK_PARITY = 3;
 | 
				
			||||||
 | 
					    public static final int SPACE_PARITY = 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Stop bits values
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static final int ONE_STOP_BIT = 1;
 | 
				
			||||||
 | 
					    public static final int ONE_POINT_FIVE_STOP_BITS = 2;
 | 
				
			||||||
 | 
					    public static final int TWO_STOP_BITS = 3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Flow control values
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static final int FLOW_CONTROL_DISABLED = 0;
 | 
				
			||||||
 | 
					    public static final int FLOW_CONTROL_RTS_ENABLED = 1;
 | 
				
			||||||
 | 
					    public static final int FLOW_CONTROL_CTS_ENABLED = 16;
 | 
				
			||||||
 | 
					    public static final int FLOW_CONTROL_DSR_ENABLED = 256;
 | 
				
			||||||
 | 
					    public static final int FLOW_CONTROL_DTR_ENABLED = 4096;
 | 
				
			||||||
 | 
					    public static final int FLOW_CONTROL_XONXOFF_IN_ENABLED = 65536;
 | 
				
			||||||
 | 
					    public static final int FLOW_CONTROL_XONXOFF_OUT_ENABLED = 1048576;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Open delay (msec)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static final int OPEN_DELAY = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Timeout
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static final public int TIMEOUT_NONBLOCKING = 0x00000000;
 | 
				
			||||||
 | 
					   	static final public int TIMEOUT_READ_SEMI_BLOCKING = 0x00000001;
 | 
				
			||||||
 | 
					   	static final public int TIMEOUT_WRITE_SEMI_BLOCKING = 0x00000010;
 | 
				
			||||||
 | 
					   	static final public int TIMEOUT_READ_BLOCKING = 0x00000100;
 | 
				
			||||||
 | 
					   	static final public int TIMEOUT_WRITE_BLOCKING = 0x00001000;
 | 
				
			||||||
 | 
					   	static final public int TIMEOUT_SCANNER = 0x00010000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the <tt>ModbusTransport</tt> instance to be used for receiving
 | 
				
			||||||
 | 
					     * and sending messages.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IOException If the port is not available or cannot be opened
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract void open() throws IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the <tt>ModbusTransport</tt> instance to be used for receiving
 | 
				
			||||||
 | 
					     * and sending messages.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a <tt>ModbusTransport</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract AbstractModbusTransport getModbusTransport();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Read a specified number of bytes from the serial port
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param buffer      Buffer to recieve bytes from the port
 | 
				
			||||||
 | 
					     * @param bytesToRead Number of bytes to read
 | 
				
			||||||
 | 
					     * @return number of currently bytes read.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract int readBytes(byte[] buffer, long bytesToRead);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Write a specified number of bytes to the serial port
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param buffer       Bytes to send to the port
 | 
				
			||||||
 | 
					     * @param bytesToWrite How many bytes to send
 | 
				
			||||||
 | 
					     * @return number of currently bytes written
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract int writeBytes(byte[] buffer, long bytesToWrite);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Bytes available to read
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return number of bytes currently available to read
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract int bytesAvailable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the connection parameters to the setting in the parameters object.
 | 
				
			||||||
 | 
					     * If set fails return the parameters object to original settings and throw
 | 
				
			||||||
 | 
					     * exception.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract void setConnectionParameters();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Close the port and clean up associated elements.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract void close();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns current baud rate
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Baud rate
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract int getBaudRate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Set new baud rate
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param newBaudRate Baud rate
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract void setBaudRate(int newBaudRate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns current data bits value
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Number of data bits
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract int getNumDataBits();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns current stop bits
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Number of stop bits
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract int getNumStopBits();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns current parity
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Parity type
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract int getParity();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns a descriptive name of the current port
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a <tt>String</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract String getDescriptivePortName();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Set port timeouts
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param newTimeoutMode  Timeout mode
 | 
				
			||||||
 | 
					     * @param newReadTimeout  Read timeout
 | 
				
			||||||
 | 
					     * @param newWriteTimeout Write timeout
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract void setComPortTimeouts(int newTimeoutMode, int newReadTimeout, int newWriteTimeout);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Reports the open status of the port.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return true if port is open, false if port is closed.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract boolean isOpen();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the timeout for this <tt>UDPMasterConnection</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the timeout as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract int getTimeout();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the timeout for this <tt>UDPMasterConnection</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param timeout the timeout as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract void setTimeout(int timeout);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns a set of all the available comm port names
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Set of comm port names
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract Set<String> getCommPorts();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,123 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.net;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.io.ModbusUDPTransport;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.net.DatagramSocket;
 | 
				
			||||||
 | 
					import java.net.InetAddress;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Interface defining a <tt>UDPTerminal</tt>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public abstract class AbstractUDPTerminal {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected InetAddress address;
 | 
				
			||||||
 | 
					    protected ModbusUDPTransport transport;
 | 
				
			||||||
 | 
					    protected boolean active;
 | 
				
			||||||
 | 
					    protected int port = Modbus.DEFAULT_PORT;
 | 
				
			||||||
 | 
					    protected int timeout = Modbus.DEFAULT_TIMEOUT;
 | 
				
			||||||
 | 
					    protected DatagramSocket socket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Gets the local adapter address
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Adapter address
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public InetAddress getAddress() {
 | 
				
			||||||
 | 
					        return address;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the local port the terminal is listening on
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Port number
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized int getPort() {
 | 
				
			||||||
 | 
					        return port;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the local port the terminal is running on
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param port Local port
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected synchronized void setPort(int port) {
 | 
				
			||||||
 | 
					        this.port = port;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Tests if this <tt>UDPSlaveTerminal</tt> is active.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return <tt>true</tt> if active, <tt>false</tt> otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean isActive() {
 | 
				
			||||||
 | 
					        return active;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the timeout in milliseconds for this <tt>UDPSlaveTerminal</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param timeout the timeout as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized void setTimeout(int timeout) {
 | 
				
			||||||
 | 
					        this.timeout = timeout;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get the transport
 | 
				
			||||||
 | 
					     * @return Transport
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusUDPTransport getTransport() {
 | 
				
			||||||
 | 
					        return transport;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Activate this <tt>UDPTerminal</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws Exception if there is a network failure.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract void activate() throws Exception;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Deactivates this <tt>UDPTerminal</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract void deactivate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sends the given message.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param msg the message as <tt>byte[]</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws Exception if sending the message fails.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract void sendMessage(byte[] msg) throws Exception;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Receives and returns a message.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the message as a newly allocated <tt>byte[]</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws Exception if receiving a message fails.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public abstract byte[] receiveMessage() throws Exception;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,189 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.net;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.util.ThreadPool;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.net.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class that implements a ModbusTCPListener.
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * If listening, it accepts incoming requests passing them on to be handled.
 | 
				
			||||||
 | 
					 * If not listening, silently drops the requests.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Julie Haugh
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ModbusTCPListener extends AbstractModbusListener {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final Logger logger = LoggerFactory.getLogger(ModbusTCPListener.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private ServerSocket serverSocket = null;
 | 
				
			||||||
 | 
					    private ThreadPool threadPool;
 | 
				
			||||||
 | 
					    private Thread listener;
 | 
				
			||||||
 | 
					    private boolean useRtuOverTcp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a ModbusTCPListener instance.<br>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param poolsize the size of the <tt>ThreadPool</tt> used to handle incoming
 | 
				
			||||||
 | 
					     *                 requests.
 | 
				
			||||||
 | 
					     * @param addr     the interface to use for listening.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusTCPListener(int poolsize, InetAddress addr) {
 | 
				
			||||||
 | 
					        this(poolsize, addr, false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a ModbusTCPListener instance.<br>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param poolsize      the size of the <tt>ThreadPool</tt> used to handle incoming
 | 
				
			||||||
 | 
					     *                      requests.
 | 
				
			||||||
 | 
					     * @param addr          the interface to use for listening.
 | 
				
			||||||
 | 
					     * @param useRtuOverTcp True if the RTU protocol should be used over TCP
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusTCPListener(int poolsize, InetAddress addr, boolean useRtuOverTcp) {
 | 
				
			||||||
 | 
					        threadPool = new ThreadPool(poolsize);
 | 
				
			||||||
 | 
					        address = addr;
 | 
				
			||||||
 | 
					        this.useRtuOverTcp = useRtuOverTcp;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * /**
 | 
				
			||||||
 | 
					     * Constructs a ModbusTCPListener instance.  This interface is created
 | 
				
			||||||
 | 
					     * to listen on the wildcard address (0.0.0.0), which will accept TCP packets
 | 
				
			||||||
 | 
					     * on all available adapters/interfaces
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param poolsize the size of the <tt>ThreadPool</tt> used to handle incoming
 | 
				
			||||||
 | 
					     *                 requests.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusTCPListener(int poolsize) {
 | 
				
			||||||
 | 
					        this(poolsize, false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * /**
 | 
				
			||||||
 | 
					     * Constructs a ModbusTCPListener instance.  This interface is created
 | 
				
			||||||
 | 
					     * to listen on the wildcard address (0.0.0.0), which will accept TCP packets
 | 
				
			||||||
 | 
					     * on all available adapters/interfaces
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param poolsize      the size of the <tt>ThreadPool</tt> used to handle incoming
 | 
				
			||||||
 | 
					     *                      requests.
 | 
				
			||||||
 | 
					     * @param useRtuOverTcp True if the RTU protocol should be used over TCP
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusTCPListener(int poolsize, boolean useRtuOverTcp) {
 | 
				
			||||||
 | 
					        threadPool = new ThreadPool(poolsize);
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            address = InetAddress.getByAddress(new byte[]{0, 0, 0, 0});
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (UnknownHostException ex) {
 | 
				
			||||||
 | 
					            // Can't happen -- size is fixed.
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this.useRtuOverTcp = useRtuOverTcp;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void setTimeout(int timeout) {
 | 
				
			||||||
 | 
					        super.setTimeout(timeout);
 | 
				
			||||||
 | 
					        if (serverSocket != null && listening) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                serverSocket.setSoTimeout(timeout);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (SocketException e) {
 | 
				
			||||||
 | 
					                logger.error("Cannot set socket timeout", e);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void run() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            /*
 | 
				
			||||||
 | 
					             * A server socket is opened with a connectivity queue of a size
 | 
				
			||||||
 | 
					             * specified in int floodProtection. Concurrent login handling under
 | 
				
			||||||
 | 
					             * normal circumstances should be alright, denial of service
 | 
				
			||||||
 | 
					             * attacks via massive parallel program logins can probably be
 | 
				
			||||||
 | 
					             * prevented.
 | 
				
			||||||
 | 
					             */
 | 
				
			||||||
 | 
					            int floodProtection = 100;
 | 
				
			||||||
 | 
					            serverSocket = new ServerSocket(port, floodProtection, address);
 | 
				
			||||||
 | 
					            serverSocket.setSoTimeout(timeout);
 | 
				
			||||||
 | 
					            logger.debug("Listening to {} (Port {})", serverSocket.toString(), port);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Catch any fatal errors and set the listening flag to false to indicate an error
 | 
				
			||||||
 | 
					        catch (Exception e) {
 | 
				
			||||||
 | 
					            error = String.format("Cannot start TCP listener - %s", e.getMessage());
 | 
				
			||||||
 | 
					            listening = false;
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        listener = Thread.currentThread();
 | 
				
			||||||
 | 
					        listening = true;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Infinite loop, taking care of resources in case of a lot of
 | 
				
			||||||
 | 
					            // parallel logins
 | 
				
			||||||
 | 
					            while (listening) {
 | 
				
			||||||
 | 
					                Socket incoming;
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    incoming = serverSocket.accept();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                catch (SocketTimeoutException e) {
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                logger.debug("Making new connection {}", incoming.toString());
 | 
				
			||||||
 | 
					                if (listening) {
 | 
				
			||||||
 | 
					                    TCPSlaveConnection slave = new TCPSlaveConnection(incoming, useRtuOverTcp);
 | 
				
			||||||
 | 
					                    slave.setTimeout(timeout);
 | 
				
			||||||
 | 
					                    threadPool.execute(new TCPConnectionHandler(this, slave));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else {
 | 
				
			||||||
 | 
					                    incoming.close();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (IOException e) {
 | 
				
			||||||
 | 
					            error = String.format("Problem starting listener - %s", e.getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void stop() {
 | 
				
			||||||
 | 
					        listening = false;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (serverSocket != null) {
 | 
				
			||||||
 | 
					                serverSocket.close();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (listener != null) {
 | 
				
			||||||
 | 
					                listener.join();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (threadPool != null) {
 | 
				
			||||||
 | 
					                threadPool.close();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (Exception ex) {
 | 
				
			||||||
 | 
					            logger.error("Error while stopping ModbusTCPListener", ex);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,124 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.net;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.ModbusIOException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.io.ModbusUDPTransport;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.net.InetAddress;
 | 
				
			||||||
 | 
					import java.net.UnknownHostException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class that implements a ModbusUDPListener.<br>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Julie Haugh
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ModbusUDPListener extends AbstractModbusListener {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final Logger logger = LoggerFactory.getLogger(ModbusUDPListener.class);
 | 
				
			||||||
 | 
					    private UDPSlaveTerminal terminal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Create a new <tt>ModbusUDPListener</tt> instance listening to the given
 | 
				
			||||||
 | 
					     * interface address.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ifc an <tt>InetAddress</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusUDPListener(InetAddress ifc) {
 | 
				
			||||||
 | 
					        address = ifc;
 | 
				
			||||||
 | 
					        listening = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new ModbusUDPListener instance. The address will be set to a
 | 
				
			||||||
 | 
					     * default value of the wildcard local address and the default Modbus port.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ModbusUDPListener() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            address = InetAddress.getByAddress(new byte[]{0, 0, 0, 0});
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (UnknownHostException e) {
 | 
				
			||||||
 | 
					            // Can't happen -- length is fixed by code.
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void setTimeout(int timeout) {
 | 
				
			||||||
 | 
					        super.setTimeout(timeout);
 | 
				
			||||||
 | 
					        if (terminal != null && listening) {
 | 
				
			||||||
 | 
					            terminal.setTimeout(timeout);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Starts this <tt>ModbusUDPListener</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void run() {
 | 
				
			||||||
 | 
					        ModbusUDPTransport transport;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (address == null) {
 | 
				
			||||||
 | 
					                terminal = new UDPSlaveTerminal(InetAddress.getByAddress(new byte[]{0, 0, 0, 0}));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else {
 | 
				
			||||||
 | 
					                terminal = new UDPSlaveTerminal(address);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            terminal.setTimeout(timeout);
 | 
				
			||||||
 | 
					            terminal.setPort(port);
 | 
				
			||||||
 | 
					            terminal.activate();
 | 
				
			||||||
 | 
					            transport = new ModbusUDPTransport(terminal);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Catch any fatal errors and set the listening flag to false to indicate an error
 | 
				
			||||||
 | 
					        catch (Exception e) {
 | 
				
			||||||
 | 
					            error = String.format("Cannot start UDP listener - %s", e.getMessage());
 | 
				
			||||||
 | 
					            listening = false;
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        listening = true;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            while (listening) {
 | 
				
			||||||
 | 
					                handleRequest(transport, this);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (ModbusIOException ex1) {
 | 
				
			||||||
 | 
					            if (!ex1.isEOF()) {
 | 
				
			||||||
 | 
					                logger.error("Exception occurred before EOF while handling request", ex1);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        finally {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                terminal.deactivate();
 | 
				
			||||||
 | 
					                transport.close();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (Exception ex) {
 | 
				
			||||||
 | 
					                // ignore
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void stop() {
 | 
				
			||||||
 | 
					        terminal.deactivate();
 | 
				
			||||||
 | 
					        listening = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,71 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.net;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.ModbusIOException;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.io.AbstractModbusTransport;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a handler for incoming Modbus/TCP requests.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class TCPConnectionHandler implements Runnable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final Logger logger = LoggerFactory.getLogger(TCPConnectionHandler.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private TCPSlaveConnection connection;
 | 
				
			||||||
 | 
					    private AbstractModbusTransport transport;
 | 
				
			||||||
 | 
					    private AbstractModbusListener listener;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new <tt>TCPConnectionHandler</tt> instance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * The connections will be handling using the <tt>ModbusCouple</tt> class
 | 
				
			||||||
 | 
					     * and a <tt>ProcessImage</tt> which provides the interface between the
 | 
				
			||||||
 | 
					     * slave implementation and the <tt>TCPSlaveConnection</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param listener the listener that handled the incoming request
 | 
				
			||||||
 | 
					     * @param connection an incoming connection.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public TCPConnectionHandler(AbstractModbusListener listener, TCPSlaveConnection connection) {
 | 
				
			||||||
 | 
					        this.listener = listener;
 | 
				
			||||||
 | 
					        this.connection = connection;
 | 
				
			||||||
 | 
					        transport = this.connection.getModbusTransport();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void run() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            do {
 | 
				
			||||||
 | 
					                listener.handleRequest(transport, listener);
 | 
				
			||||||
 | 
					            } while (!Thread.currentThread().isInterrupted());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (ModbusIOException ex) {
 | 
				
			||||||
 | 
					            if (!ex.isEOF()) {
 | 
				
			||||||
 | 
					                logger.debug(ex.getMessage());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        finally {
 | 
				
			||||||
 | 
					            connection.close();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,338 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.net;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.io.AbstractModbusTransport;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.io.ModbusRTUTCPTransport;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.io.ModbusTCPTransport;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.util.ModbusUtil;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.net.InetAddress;
 | 
				
			||||||
 | 
					import java.net.InetSocketAddress;
 | 
				
			||||||
 | 
					import java.net.Socket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class that implements a TCPMasterConnection.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Julie Haugh
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class TCPMasterConnection {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final Logger logger = LoggerFactory.getLogger(TCPMasterConnection.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // instance attributes
 | 
				
			||||||
 | 
					    private Socket socket;
 | 
				
			||||||
 | 
					    private int timeout = Modbus.DEFAULT_TIMEOUT;
 | 
				
			||||||
 | 
					    private boolean connected;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private InetAddress address;
 | 
				
			||||||
 | 
					    private int port = Modbus.DEFAULT_PORT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private ModbusTCPTransport transport;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private boolean useRtuOverTcp = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * useUrgentData - sent a byte of urgent data when testing the TCP
 | 
				
			||||||
 | 
					     * connection.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private boolean useUrgentData = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a <tt>TCPMasterConnection</tt> instance with a given
 | 
				
			||||||
 | 
					     * destination address.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param adr the destination <tt>InetAddress</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public TCPMasterConnection(InetAddress adr) {
 | 
				
			||||||
 | 
					        address = adr;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Prepares the associated <tt>ModbusTransport</tt> of this
 | 
				
			||||||
 | 
					     * <tt>TCPMasterConnection</tt> for use.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param useRtuOverTcp True if the RTU protocol should be used over TCP
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws IOException if an I/O related error occurs.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void prepareTransport(boolean useRtuOverTcp) throws IOException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // If we don't have a transport, or the transport type has changed
 | 
				
			||||||
 | 
					        if (transport == null || (this.useRtuOverTcp != useRtuOverTcp)) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Save the flag to tell us which transport type to use
 | 
				
			||||||
 | 
					            this.useRtuOverTcp = useRtuOverTcp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Select the correct transport
 | 
				
			||||||
 | 
					            if (useRtuOverTcp) {
 | 
				
			||||||
 | 
					                logger.trace("prepareTransport() -> using RTU over TCP transport.");
 | 
				
			||||||
 | 
					                transport = new ModbusRTUTCPTransport(socket);
 | 
				
			||||||
 | 
					                transport.setMaster(this);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else {
 | 
				
			||||||
 | 
					                logger.trace("prepareTransport() -> using standard TCP transport.");
 | 
				
			||||||
 | 
					                transport = new ModbusTCPTransport(socket);
 | 
				
			||||||
 | 
					                transport.setMaster(this);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            logger.trace("prepareTransport() -> using custom transport: {}", transport.getClass().getSimpleName());
 | 
				
			||||||
 | 
					            transport.setSocket(socket);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        transport.setTimeout(timeout);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Opens this <tt>TCPMasterConnection</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws Exception if there is a network failure.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized void connect() throws Exception {
 | 
				
			||||||
 | 
					        connect(useRtuOverTcp);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Opens this <tt>TCPMasterConnection</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param useRtuOverTcp True if the RTU protocol should be used over TCP
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws Exception if there is a network failure.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized void connect(boolean useRtuOverTcp) throws Exception {
 | 
				
			||||||
 | 
					        if (!isConnected()) {
 | 
				
			||||||
 | 
					            logger.debug("connect()");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Create a socket without auto-connecting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            socket = new Socket();
 | 
				
			||||||
 | 
					            socket.setReuseAddress(true);
 | 
				
			||||||
 | 
					            socket.setSoLinger(true, 1);
 | 
				
			||||||
 | 
					            socket.setKeepAlive(true);
 | 
				
			||||||
 | 
					            setTimeout(timeout);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Connect - only wait for the timeout number of milliseconds
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            socket.connect(new InetSocketAddress(address, port), timeout);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Prepare the transport
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            prepareTransport(useRtuOverTcp);
 | 
				
			||||||
 | 
					            connected = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Tests if this <tt>TCPMasterConnection</tt> is connected.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return <tt>true</tt> if connected, <tt>false</tt> otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized boolean isConnected() {
 | 
				
			||||||
 | 
					        if (connected && socket != null) {
 | 
				
			||||||
 | 
					            if (!socket.isConnected() || socket.isClosed() || socket.isInputShutdown() || socket.isOutputShutdown()) {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    socket.close();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                catch (IOException e) {
 | 
				
			||||||
 | 
					                    logger.error("Socket exception", e);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                finally {
 | 
				
			||||||
 | 
					                    connected = false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else {
 | 
				
			||||||
 | 
					                /*
 | 
				
			||||||
 | 
					                 * When useUrgentData is set, a byte of urgent data
 | 
				
			||||||
 | 
					                 * will be sent to the server to test the connection. If
 | 
				
			||||||
 | 
					                 * the connection is actually broken, an IException will
 | 
				
			||||||
 | 
					                 * occur and the connection will be closed.
 | 
				
			||||||
 | 
					                 *
 | 
				
			||||||
 | 
					                 * Note: RFC 6093 has decreed that we stop using urgent
 | 
				
			||||||
 | 
					                 * data.
 | 
				
			||||||
 | 
					                 */
 | 
				
			||||||
 | 
					                if (useUrgentData) {
 | 
				
			||||||
 | 
					                    try {
 | 
				
			||||||
 | 
					                        socket.sendUrgentData(0);
 | 
				
			||||||
 | 
					                        ModbusUtil.sleep(5);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    catch (IOException e) {
 | 
				
			||||||
 | 
					                        connected = false;
 | 
				
			||||||
 | 
					                        try {
 | 
				
			||||||
 | 
					                            socket.close();
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        catch (IOException e1) {
 | 
				
			||||||
 | 
					                            // Do nothing.
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return connected;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Closes this <tt>TCPMasterConnection</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void close() {
 | 
				
			||||||
 | 
					        if (connected) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                transport.close();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (IOException ex) {
 | 
				
			||||||
 | 
					                logger.debug("close()", ex);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            finally {
 | 
				
			||||||
 | 
					                connected = false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the <tt>ModbusTransport</tt> associated with this
 | 
				
			||||||
 | 
					     * <tt>TCPMasterConnection</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the connection's <tt>ModbusTransport</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public AbstractModbusTransport getModbusTransport() {
 | 
				
			||||||
 | 
					        return transport;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Set the <tt>ModbusTransport</tt> associated with this
 | 
				
			||||||
 | 
					     * <tt>TCPMasterConnection</tt>
 | 
				
			||||||
 | 
					     * @param trans associated transport
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setModbusTransport(ModbusTCPTransport trans) {
 | 
				
			||||||
 | 
					        transport = trans;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the timeout (msec) for this <tt>TCPMasterConnection</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the timeout as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized int getTimeout() {
 | 
				
			||||||
 | 
					        return timeout;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the timeout (msec) for this <tt>TCPMasterConnection</tt>. This is both the
 | 
				
			||||||
 | 
					     * connection timeout and the transaction timeout
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param timeout - the timeout in milliseconds as an <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized void setTimeout(int timeout) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            this.timeout = timeout;
 | 
				
			||||||
 | 
					            if (socket != null) {
 | 
				
			||||||
 | 
					                socket.setSoTimeout(timeout);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (IOException ex) {
 | 
				
			||||||
 | 
					            logger.warn("Could not set timeout to value " + timeout, ex);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the destination port of this <tt>TCPMasterConnection</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the port number as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getPort() {
 | 
				
			||||||
 | 
					        return port;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the destination port of this <tt>TCPMasterConnection</tt>. The
 | 
				
			||||||
 | 
					     * default is defined as <tt>Modbus.DEFAULT_PORT</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param port the port number as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setPort(int port) {
 | 
				
			||||||
 | 
					        this.port = port;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the destination <tt>InetAddress</tt> of this
 | 
				
			||||||
 | 
					     * <tt>TCPMasterConnection</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the destination address as <tt>InetAddress</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public InetAddress getAddress() {
 | 
				
			||||||
 | 
					        return address;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the destination <tt>InetAddress</tt> of this
 | 
				
			||||||
 | 
					     * <tt>TCPMasterConnection</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param adr the destination address as <tt>InetAddress</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setAddress(InetAddress adr) {
 | 
				
			||||||
 | 
					        address = adr;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Gets the current setting of the flag which controls sending
 | 
				
			||||||
 | 
					     * urgent data to test a network connection.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Status
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean getUseUrgentData() {
 | 
				
			||||||
 | 
					        return useUrgentData;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Set the flag which controls sending urgent data to test a
 | 
				
			||||||
 | 
					     * network connection.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param useUrgentData - Connections are testing using urgent data.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setUseUrgentData(boolean useUrgentData) {
 | 
				
			||||||
 | 
					        this.useUrgentData = useUrgentData;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns true if this connection is an RTU over TCP type
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @return True if RTU over TCP
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean isUseRtuOverTcp() {
 | 
				
			||||||
 | 
					        return useRtuOverTcp;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the transport type to use
 | 
				
			||||||
 | 
					     * Normally set during the connection but can also be set after a connection has been established
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param useRtuOverTcp True if the transport should be interpreted as RTU over tCP
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws Exception If the connection is not valid
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setUseRtuOverTcp(boolean useRtuOverTcp) throws Exception {
 | 
				
			||||||
 | 
					        this.useRtuOverTcp = useRtuOverTcp;
 | 
				
			||||||
 | 
					        if (isConnected()) {
 | 
				
			||||||
 | 
					            prepareTransport(useRtuOverTcp);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,180 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.net;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.io.AbstractModbusTransport;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.io.ModbusRTUTCPTransport;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.io.ModbusTCPTransport;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.net.InetAddress;
 | 
				
			||||||
 | 
					import java.net.Socket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class that implements a TCPSlaveConnection.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class TCPSlaveConnection {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final Logger logger = LoggerFactory.getLogger(TCPSlaveConnection.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // instance attributes
 | 
				
			||||||
 | 
					    private Socket socket;
 | 
				
			||||||
 | 
					    private int timeout = Modbus.DEFAULT_TIMEOUT;
 | 
				
			||||||
 | 
					    private boolean connected;
 | 
				
			||||||
 | 
					    private ModbusTCPTransport transport;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a <tt>TCPSlaveConnection</tt> instance using a given socket
 | 
				
			||||||
 | 
					     * instance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param socket the socket instance to be used for communication.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public TCPSlaveConnection(Socket socket) {
 | 
				
			||||||
 | 
					        this(socket, false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a <tt>TCPSlaveConnection</tt> instance using a given socket
 | 
				
			||||||
 | 
					     * instance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param socket        the socket instance to be used for communication.
 | 
				
			||||||
 | 
					     * @param useRtuOverTcp True if the RTU protocol should be used over TCP
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public TCPSlaveConnection(Socket socket, boolean useRtuOverTcp) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            setSocket(socket, useRtuOverTcp);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (IOException ex) {
 | 
				
			||||||
 | 
					            logger.debug("TCPSlaveConnection::Socket invalid");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            throw new IllegalStateException("Socket invalid", ex);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Closes this <tt>TCPSlaveConnection</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void close() {
 | 
				
			||||||
 | 
					        if (connected) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                transport.close();
 | 
				
			||||||
 | 
					                socket.close();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (IOException ex) {
 | 
				
			||||||
 | 
					                logger.warn("Could not close socket", ex);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            connected = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the <tt>ModbusTransport</tt> associated with this
 | 
				
			||||||
 | 
					     * <tt>TCPMasterConnection</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the connection's <tt>ModbusTransport</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public AbstractModbusTransport getModbusTransport() {
 | 
				
			||||||
 | 
					        return transport;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Prepares the associated <tt>ModbusTransport</tt> of this
 | 
				
			||||||
 | 
					     * <tt>TCPMasterConnection</tt> for use.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param socket        the socket to be used for communication.
 | 
				
			||||||
 | 
					     * @param useRtuOverTcp True if the RTU protocol should be used over TCP
 | 
				
			||||||
 | 
					     * @throws IOException if an I/O related error occurs.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void setSocket(Socket socket, boolean useRtuOverTcp) throws IOException {
 | 
				
			||||||
 | 
					        this.socket = socket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (transport == null) {
 | 
				
			||||||
 | 
					            if (useRtuOverTcp) {
 | 
				
			||||||
 | 
					                logger.trace("setSocket() -> using RTU over TCP transport.");
 | 
				
			||||||
 | 
					                transport = new ModbusRTUTCPTransport(socket);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else {
 | 
				
			||||||
 | 
					                logger.trace("setSocket() -> using standard TCP transport.");
 | 
				
			||||||
 | 
					                transport = new ModbusTCPTransport(socket);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            transport.setSocket(socket);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        connected = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the timeout for this <tt>TCPSlaveConnection</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the timeout as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getTimeout() {
 | 
				
			||||||
 | 
					        return timeout;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the timeout for this <tt>TCPSlaveConnection</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param timeout the timeout in milliseconds as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setTimeout(int timeout) {
 | 
				
			||||||
 | 
					        this.timeout = timeout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            socket.setSoTimeout(timeout);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (IOException ex) {
 | 
				
			||||||
 | 
					            logger.warn("Could not set timeout to " + timeout, ex);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the destination port of this <tt>TCPSlaveConnection</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the port number as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getPort() {
 | 
				
			||||||
 | 
					        return socket.getLocalPort();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the destination <tt>InetAddress</tt> of this
 | 
				
			||||||
 | 
					     * <tt>TCPSlaveConnection</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the destination address as <tt>InetAddress</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public InetAddress getAddress() {
 | 
				
			||||||
 | 
					        return socket.getLocalAddress();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Tests if this <tt>TCPSlaveConnection</tt> is connected.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return <tt>true</tt> if connected, <tt>false</tt> otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean isConnected() {
 | 
				
			||||||
 | 
					        return connected;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,174 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.net;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.Modbus;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.io.AbstractModbusTransport;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.net.InetAddress;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class that implements a UDPMasterConnection.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class UDPMasterConnection {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final Logger logger = LoggerFactory.getLogger(UDPMasterConnection.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //instance attributes
 | 
				
			||||||
 | 
					    private UDPMasterTerminal terminal;
 | 
				
			||||||
 | 
					    private int timeout = Modbus.DEFAULT_TIMEOUT;
 | 
				
			||||||
 | 
					    private boolean connected;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private InetAddress address;
 | 
				
			||||||
 | 
					    private int port = Modbus.DEFAULT_PORT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a <tt>UDPMasterConnection</tt> instance
 | 
				
			||||||
 | 
					     * with a given destination address.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param adr the destination <tt>InetAddress</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public UDPMasterConnection(InetAddress adr) {
 | 
				
			||||||
 | 
					        address = adr;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Opens this <tt>UDPMasterConnection</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws Exception if there is a network failure.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized void connect() throws Exception {
 | 
				
			||||||
 | 
					        if (!connected) {
 | 
				
			||||||
 | 
					            terminal = new UDPMasterTerminal(address);
 | 
				
			||||||
 | 
					            terminal.setPort(port);
 | 
				
			||||||
 | 
					            terminal.setTimeout(timeout);
 | 
				
			||||||
 | 
					            terminal.activate();
 | 
				
			||||||
 | 
					            connected = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Closes this <tt>UDPMasterConnection</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void close() {
 | 
				
			||||||
 | 
					        if (connected) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                terminal.deactivate();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (Exception ex) {
 | 
				
			||||||
 | 
					                logger.debug("Exception occurred while closing UDPMasterConnection", ex);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            connected = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the <tt>ModbusTransport</tt> associated with this
 | 
				
			||||||
 | 
					     * <tt>UDPMasterConnection</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the connection's <tt>ModbusTransport</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public AbstractModbusTransport getModbusTransport() {
 | 
				
			||||||
 | 
					        return terminal == null ? null : terminal.getTransport();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the terminal used for handling the package traffic.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a <tt>UDPTerminal</tt> instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public AbstractUDPTerminal getTerminal() {
 | 
				
			||||||
 | 
					        return terminal;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the timeout for this <tt>UDPMasterConnection</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the timeout as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized int getTimeout() {
 | 
				
			||||||
 | 
					        return timeout;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the timeout for this <tt>UDPMasterConnection</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param timeout the timeout as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public synchronized void setTimeout(int timeout) {
 | 
				
			||||||
 | 
					        this.timeout = timeout;
 | 
				
			||||||
 | 
					        if (terminal != null) {
 | 
				
			||||||
 | 
					            terminal.setTimeout(timeout);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the destination port of this
 | 
				
			||||||
 | 
					     * <tt>UDPMasterConnection</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the port number as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getPort() {
 | 
				
			||||||
 | 
					        return port;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the destination port of this
 | 
				
			||||||
 | 
					     * <tt>UDPMasterConnection</tt>.
 | 
				
			||||||
 | 
					     * The default is defined as <tt>Modbus.DEFAULT_PORT</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param port the port number as <tt>int</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setPort(int port) {
 | 
				
			||||||
 | 
					        this.port = port;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the destination <tt>InetAddress</tt> of this
 | 
				
			||||||
 | 
					     * <tt>UDPMasterConnection</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the destination address as <tt>InetAddress</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public InetAddress getAddress() {
 | 
				
			||||||
 | 
					        return address;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the destination <tt>InetAddress</tt> of this
 | 
				
			||||||
 | 
					     * <tt>UDPMasterConnection</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param adr the destination address as <tt>InetAddress</tt>.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setAddress(InetAddress adr) {
 | 
				
			||||||
 | 
					        address = adr;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Tests if this <tt>UDPMasterConnection</tt> is connected.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return <tt>true</tt> if connected, <tt>false</tt> otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean isConnected() {
 | 
				
			||||||
 | 
					        return connected;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,104 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.net;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.io.ModbusUDPTransport;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.net.DatagramPacket;
 | 
				
			||||||
 | 
					import java.net.DatagramSocket;
 | 
				
			||||||
 | 
					import java.net.InetAddress;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>UDPMasterTerminal</tt>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class UDPMasterTerminal extends AbstractUDPTerminal {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final Logger logger = LoggerFactory.getLogger(UDPMasterTerminal.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Create a UDP master connection to the specified Internet address.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param addr Remote address to connect to
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    UDPMasterTerminal(InetAddress addr) {
 | 
				
			||||||
 | 
					        address = addr;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Create an uninitialized UDP master connection.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public UDPMasterTerminal() {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public synchronized void activate() throws Exception {
 | 
				
			||||||
 | 
					        if (!isActive()) {
 | 
				
			||||||
 | 
					            if (socket == null) {
 | 
				
			||||||
 | 
					                socket = new DatagramSocket();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            logger.debug("UDPMasterTerminal::haveSocket():{}", socket.toString());
 | 
				
			||||||
 | 
					            logger.debug("UDPMasterTerminal::raddr=:{}:rport:{}", address.toString(), port);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            socket.setReceiveBufferSize(1024);
 | 
				
			||||||
 | 
					            socket.setSendBufferSize(1024);
 | 
				
			||||||
 | 
					            socket.setSoTimeout(timeout);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            transport = new ModbusUDPTransport(this);
 | 
				
			||||||
 | 
					            active = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        logger.debug("UDPMasterTerminal::activated");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public synchronized void deactivate() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            logger.debug("UDPMasterTerminal::deactivate()");
 | 
				
			||||||
 | 
					            if (socket != null) {
 | 
				
			||||||
 | 
					                socket.close();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            transport = null;
 | 
				
			||||||
 | 
					            active = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (Exception ex) {
 | 
				
			||||||
 | 
					            logger.error("Error closing socket", ex);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public synchronized void sendMessage(byte[] msg) throws Exception {
 | 
				
			||||||
 | 
					        DatagramPacket req = new DatagramPacket(msg, msg.length, address, port);
 | 
				
			||||||
 | 
					        socket.send(req);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public synchronized byte[] receiveMessage() throws Exception {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // The longest possible DatagramPacket is 256 bytes (Modbus message
 | 
				
			||||||
 | 
					        // limit) plus the 6 byte header.
 | 
				
			||||||
 | 
					        byte[] buffer = new byte[262];
 | 
				
			||||||
 | 
					        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
 | 
				
			||||||
 | 
					        socket.setSoTimeout(timeout);
 | 
				
			||||||
 | 
					        socket.receive(packet);
 | 
				
			||||||
 | 
					        return buffer;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,250 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.net;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.io.ModbusUDPTransport;
 | 
				
			||||||
 | 
					import com.ghgande.j2mod.modbus.util.ModbusUtil;
 | 
				
			||||||
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.net.DatagramPacket;
 | 
				
			||||||
 | 
					import java.net.DatagramSocket;
 | 
				
			||||||
 | 
					import java.net.InetAddress;
 | 
				
			||||||
 | 
					import java.util.Hashtable;
 | 
				
			||||||
 | 
					import java.util.concurrent.LinkedBlockingQueue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Class implementing a <tt>UDPSlaveTerminal</tt>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class UDPSlaveTerminal extends AbstractUDPTerminal {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final Logger logger = LoggerFactory.getLogger(UDPSlaveTerminal.class);
 | 
				
			||||||
 | 
					    protected Hashtable<Integer, DatagramPacket> requests = new Hashtable<Integer, DatagramPacket>(342);
 | 
				
			||||||
 | 
					    private LinkedBlockingQueue<byte[]> sendQueue = new LinkedBlockingQueue<byte[]>();
 | 
				
			||||||
 | 
					    private LinkedBlockingQueue<byte[]> receiveQueue = new LinkedBlockingQueue<byte[]>();
 | 
				
			||||||
 | 
					    private PacketSender packetSender;
 | 
				
			||||||
 | 
					    private PacketReceiver packetReceiver;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates a slave terminal on the specified adapter address
 | 
				
			||||||
 | 
					     * Use 0.0.0.0 to listen on all adapters
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param localaddress Local address to bind to
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected UDPSlaveTerminal(InetAddress localaddress) {
 | 
				
			||||||
 | 
					        address = localaddress;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public synchronized void activate() throws Exception {
 | 
				
			||||||
 | 
					        if (!isActive()) {
 | 
				
			||||||
 | 
					            logger.debug("UDPSlaveTerminal.activate()");
 | 
				
			||||||
 | 
					            if (address != null && port != -1) {
 | 
				
			||||||
 | 
					                socket = new DatagramSocket(port, address);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else {
 | 
				
			||||||
 | 
					                socket = new DatagramSocket();
 | 
				
			||||||
 | 
					                port = socket.getLocalPort();
 | 
				
			||||||
 | 
					                address = socket.getLocalAddress();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            logger.debug("UDPSlaveTerminal::haveSocket():{}", socket.toString());
 | 
				
			||||||
 | 
					            logger.debug("UDPSlaveTerminal::addr=:{}:port={}", address.toString(), port);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            socket.setReceiveBufferSize(1024);
 | 
				
			||||||
 | 
					            socket.setSendBufferSize(1024);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Never timeout the receive
 | 
				
			||||||
 | 
					            socket.setSoTimeout(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Start a sender
 | 
				
			||||||
 | 
					            packetSender = new PacketSender(socket);
 | 
				
			||||||
 | 
					            new Thread(packetSender).start();
 | 
				
			||||||
 | 
					            logger.debug("UDPSlaveTerminal::sender started()");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Start a receiver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            packetReceiver = new PacketReceiver(socket);
 | 
				
			||||||
 | 
					            new Thread(packetReceiver).start();
 | 
				
			||||||
 | 
					            logger.debug("UDPSlaveTerminal::receiver started()");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Create a transport to use
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            transport = new ModbusUDPTransport(this);
 | 
				
			||||||
 | 
					            logger.debug("UDPSlaveTerminal::transport created");
 | 
				
			||||||
 | 
					            active = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        logger.debug("UDPSlaveTerminal::activated");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public synchronized void deactivate() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (active) {
 | 
				
			||||||
 | 
					                // Stop receiver - this will stop and close the socket
 | 
				
			||||||
 | 
					                packetReceiver.stop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Stop sender gracefully
 | 
				
			||||||
 | 
					                packetSender.stop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                transport = null;
 | 
				
			||||||
 | 
					                active = false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (Exception ex) {
 | 
				
			||||||
 | 
					            logger.error("Error deactivating UDPSlaveTerminal", ex);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void sendMessage(byte[] msg) throws Exception {
 | 
				
			||||||
 | 
					        sendQueue.add(msg);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public byte[] receiveMessage() throws Exception {
 | 
				
			||||||
 | 
					        return receiveQueue.take();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The background thread that is responsible for sending messages in response to requests
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    class PacketSender implements Runnable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private boolean running;
 | 
				
			||||||
 | 
					        private boolean closed;
 | 
				
			||||||
 | 
					        private Thread thread;
 | 
				
			||||||
 | 
					        private DatagramSocket socket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * Constructs a sender with th socket
 | 
				
			||||||
 | 
					         *
 | 
				
			||||||
 | 
					         * @param socket Socket to use
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        public PacketSender(DatagramSocket socket) {
 | 
				
			||||||
 | 
					            running = true;
 | 
				
			||||||
 | 
					            this.socket = socket;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * Stops the sender
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        public void stop() {
 | 
				
			||||||
 | 
					            running = false;
 | 
				
			||||||
 | 
					            thread.interrupt();
 | 
				
			||||||
 | 
					            while (!closed) {
 | 
				
			||||||
 | 
					                ModbusUtil.sleep(100);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * Thread loop that sends messages
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        public void run() {
 | 
				
			||||||
 | 
					            closed = false;
 | 
				
			||||||
 | 
					            thread = Thread.currentThread();
 | 
				
			||||||
 | 
					            do {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    // Pickup the message and corresponding request
 | 
				
			||||||
 | 
					                    byte[] message = sendQueue.take();
 | 
				
			||||||
 | 
					                    DatagramPacket req = requests.remove(ModbusUtil.registersToInt(message));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // Create new Package with corresponding address and port
 | 
				
			||||||
 | 
					                    if (req != null) {
 | 
				
			||||||
 | 
					                        DatagramPacket res = new DatagramPacket(message, message.length, req.getAddress(), req.getPort());
 | 
				
			||||||
 | 
					                        socket.send(res);
 | 
				
			||||||
 | 
					                        logger.debug("Sent package from queue");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                catch (Exception ex) {
 | 
				
			||||||
 | 
					                    // Ignore the error if we are no longer listening
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (running) {
 | 
				
			||||||
 | 
					                        logger.error("Problem reading UDP socket", ex);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } while (running);
 | 
				
			||||||
 | 
					            closed = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The background thread that receives messages and adds them to the process list
 | 
				
			||||||
 | 
					     * for further analysis
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    class PacketReceiver implements Runnable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private boolean running;
 | 
				
			||||||
 | 
					        private boolean closed;
 | 
				
			||||||
 | 
					        private DatagramSocket socket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * A receiver thread for reception of UDP messages
 | 
				
			||||||
 | 
					         *
 | 
				
			||||||
 | 
					         * @param socket Socket to use
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        public PacketReceiver(DatagramSocket socket) {
 | 
				
			||||||
 | 
					            running = true;
 | 
				
			||||||
 | 
					            this.socket = socket;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * Stops the thread
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        public void stop() {
 | 
				
			||||||
 | 
					            running = false;
 | 
				
			||||||
 | 
					            socket.close();
 | 
				
			||||||
 | 
					            while (!closed) {
 | 
				
			||||||
 | 
					                ModbusUtil.sleep(100);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * Background thread for reading UDP messages
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        public void run() {
 | 
				
			||||||
 | 
					            closed = false;
 | 
				
			||||||
 | 
					            do {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    // 1. Prepare buffer and receive package
 | 
				
			||||||
 | 
					                    byte[] buffer = new byte[256];// max size
 | 
				
			||||||
 | 
					                    DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
 | 
				
			||||||
 | 
					                    socket.receive(packet);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // 2. Extract TID and remember request
 | 
				
			||||||
 | 
					                    Integer tid = ModbusUtil.registersToInt(buffer);
 | 
				
			||||||
 | 
					                    requests.put(tid, packet);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // 3. place the data buffer in the queue
 | 
				
			||||||
 | 
					                    receiveQueue.put(buffer);
 | 
				
			||||||
 | 
					                    logger.debug("Received package to queue");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                catch (Exception ex) {
 | 
				
			||||||
 | 
					                    // Ignore the error if we are no longer listening
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (running) {
 | 
				
			||||||
 | 
					                        logger.error("Problem reading UDP socket", ex);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } while (running);
 | 
				
			||||||
 | 
					            closed = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,70 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.procimg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Abstract class for a register.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public abstract class AbstractRegister implements Register {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The word (<tt>byte[2]</tt>) holding the register content.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected byte[] register = new byte[2];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int getValue() {
 | 
				
			||||||
 | 
					        return ((register[0] & 0xff) << 8 | (register[1] & 0xff));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public final int toUnsignedShort() {
 | 
				
			||||||
 | 
					        return ((register[0] & 0xff) << 8 | (register[1] & 0xff));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public final short toShort() {
 | 
				
			||||||
 | 
					        return (short)((register[0] << 8) | (register[1] & 0xff));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public synchronized byte[] toBytes() {
 | 
				
			||||||
 | 
					        byte[] dest = new byte[register.length];
 | 
				
			||||||
 | 
					        System.arraycopy(register, 0, dest, 0, dest.length);
 | 
				
			||||||
 | 
					        return dest;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public final void setValue(short s) {
 | 
				
			||||||
 | 
					        register[0] = (byte)(0xff & (s >> 8));
 | 
				
			||||||
 | 
					        register[1] = (byte)(0xff & s);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public final void setValue(byte[] bytes) {
 | 
				
			||||||
 | 
					        if (bytes.length < 2) {
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            register[0] = bytes[0];
 | 
				
			||||||
 | 
					            register[1] = bytes[1];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public final void setValue(int v) {
 | 
				
			||||||
 | 
					        register[0] = (byte)(0xff & (v >> 8));
 | 
				
			||||||
 | 
					        register[1] = (byte)(0xff & v);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,140 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.procimg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * The default ProcessImageFactory. It creates a new <tt>SimpleProcessImage</tt>
 | 
				
			||||||
 | 
					 * each time <tt>createProcessImageImplementation()</tt> is invoked.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author jfhaugh
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class DefaultProcessImageFactory implements ProcessImageFactory {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns a new SimpleProcessImage instance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a SimpleProcessImage instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ProcessImageImplementation createProcessImageImplementation() {
 | 
				
			||||||
 | 
					        return new SimpleProcessImage();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns a new SimpleDigitalIn instance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a SimpleDigitalIn instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public DigitalIn createDigitalIn() {
 | 
				
			||||||
 | 
					        return new SimpleDigitalIn();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns a new DigitalIn instance with the given state.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param state true if set, false otherwise.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a SimpleDigitalIn instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public DigitalIn createDigitalIn(boolean state) {
 | 
				
			||||||
 | 
					        return new SimpleDigitalIn(state);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns a new SimpleDigitalOut instance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a SimpleDigitalOut instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public DigitalOut createDigitalOut() {
 | 
				
			||||||
 | 
					        return new SimpleDigitalOut();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns a new DigitalOut instance with the given state.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param b true if set, false otherwise.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a SimpleDigitalOut instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public DigitalOut createDigitalOut(boolean b) {
 | 
				
			||||||
 | 
					        return new SimpleDigitalOut(b);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns a new SimpleInputRegister instance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a SimpleInputRegister instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public InputRegister createInputRegister() {
 | 
				
			||||||
 | 
					        return new SimpleInputRegister();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns a new InputRegister instance with a given value.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param b1 the first <tt>byte</tt>.
 | 
				
			||||||
 | 
					     * @param b2 the second <tt>byte</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return an InputRegister instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public InputRegister createInputRegister(byte b1, byte b2) {
 | 
				
			||||||
 | 
					        return new SimpleInputRegister(b1, b2);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates a new SimpleRegister instance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a SimpleRegister instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public Register createRegister() {
 | 
				
			||||||
 | 
					        return new SimpleRegister();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns a new Register instance with a given value.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param b1 the first <tt>byte</tt>.
 | 
				
			||||||
 | 
					     * @param b2 the second <tt>byte</tt>.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return a Register instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public Register createRegister(byte b1, byte b2) {
 | 
				
			||||||
 | 
					        return new SimpleRegister(b1, b2);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns a new InputRegister instance with a given value.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param value the value of the register as an <tt>int</tt>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return an InputRegister instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public InputRegister createInputRegister(int value) {
 | 
				
			||||||
 | 
					        return new SimpleInputRegister(value);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates a new SimpleRegister instance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param value initial value of the register
 | 
				
			||||||
 | 
					     * @return a SimpleRegister instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public Register createRegister(int value) {
 | 
				
			||||||
 | 
					        return new SimpleRegister(value);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2002-2016 jamod & j2mod development teams
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed 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 com.ghgande.j2mod.modbus.procimg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Interface defining a digital input.
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * In Modbus terms this represents an
 | 
				
			||||||
 | 
					 * input discrete, it is read only from
 | 
				
			||||||
 | 
					 * the slave side.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Dieter Wimberger
 | 
				
			||||||
 | 
					 * @author Steve O'Hara (4NG)
 | 
				
			||||||
 | 
					 * @version 2.0 (March 2016)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public interface DigitalIn {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Tests if this <tt>DigitalIn</tt> is set.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return true if set, false otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    boolean isSet();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user