Neueste Version vom alten Gitlab-Stand auf dem priv. Server

This commit is contained in:
Patrick Tschuchnig 2018-12-18 15:04:41 +01:00
parent 0a95dc7e29
commit ff921e539e
117 changed files with 19241 additions and 0 deletions

View File

@ -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());
}
}

View File

@ -0,0 +1,188 @@
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.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
public class DisplayMessageActivity extends AppCompatActivity {
private Handler handlerCoil;
private Handler handlerInputRegister;
private Handler handlerDiscreteInput;
private GlobalState state;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_display_message);
// listener
LocalBroadcastManager.getInstance(this).registerReceiver(coilMessageReceiver, new IntentFilter("readOnlyCoil"));
LocalBroadcastManager.getInstance(this).registerReceiver(discreteInputMessageReceiver, new IntentFilter("readOnlyDiscreteInput"));
LocalBroadcastManager.getInstance(this).registerReceiver(inputRegisterMessageReceiver, new IntentFilter("readOnlyInputRegister"));
state = (GlobalState) getApplicationContext();
Toast toast = Toast.makeText(getApplicationContext(), "This is my toast !", Toast.LENGTH_LONG);
toast.show();
//handler
handlerCoil = new Handler();
handlerCoil.post(refreshCoil);
handlerInputRegister = new Handler();
handlerInputRegister.post(refreshInputRegister);
handlerDiscreteInput = new Handler();
handlerDiscreteInput.post(refreshDiscreteInput);
}
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", GlobalState.InputConfig.Q_COIL.getStartReference());
serviceIntent.putExtra("extra.count", GlobalState.InputConfig.Q_COIL.getCount());
serviceIntent.putExtra("extra.intent.name", "readOnlyCoil");
getApplicationContext().startService(serviceIntent);
handlerCoil.postDelayed(this, 2000);
}
};
Runnable refreshDiscreteInput = new Runnable() {
@Override
public void run() {
Intent serviceIntent = new Intent(getApplicationContext(), MyIntentService.class);
serviceIntent.setAction("read.discrete.input");
serviceIntent.putExtra("extra.ip.address", state.getIpAddress());
serviceIntent.putExtra("extra.ip.port", state.getPort());
serviceIntent.putExtra("extra.ref", GlobalState.InputConfig.I_DI.getStartReference());
serviceIntent.putExtra("extra.count", GlobalState.InputConfig.I_DI.getCount());
serviceIntent.putExtra("extra.intent.name", "readOnlyDiscreteInput");
getApplicationContext().startService(serviceIntent);
handlerDiscreteInput.postDelayed(this, 5000);
}
};
Runnable refreshInputRegister = new Runnable() {
@Override
public void run() {
Intent serviceIntent = new Intent(getApplicationContext(), MyIntentService.class);
serviceIntent.setAction("read.input.register");
serviceIntent.putExtra("extra.ip.address", state.getIpAddress());
serviceIntent.putExtra("extra.ip.port", state.getPort());
serviceIntent.putExtra("extra.ref", GlobalState.InputConfig.AI_IR.getStartReference());
serviceIntent.putExtra("extra.count", GlobalState.InputConfig.AI_IR.getCount());
serviceIntent.putExtra("extra.intent.name", "readOnlyInputRegister");
getApplicationContext().startService(serviceIntent);
handlerInputRegister.postDelayed(this, 1000);
}
};
// on message receive, update the view with values
private BroadcastReceiver coilMessageReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Bundle bundle = intent.getExtras();
boolean[] booleanArray = bundle.getBooleanArray("coilValues");
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());
}
};
private BroadcastReceiver inputRegisterMessageReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Bundle bundle = intent.getExtras();
ArrayList<Integer> integerArrayList = bundle.getIntegerArrayList("irValues");
TextView textView9 = findViewById(R.id.textView9);
textView9.setText(integerArrayList.get(0).toString());
}
};
private BroadcastReceiver discreteInputMessageReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Bundle bundle = intent.getExtras();
boolean[] booleanArray = bundle.getBooleanArray("discreteInputValues");
TextView textView10 = findViewById(R.id.textView10);
textView10.setText(new Boolean(booleanArray[0]).toString());
TextView textView11 = findViewById(R.id.h2_textview);
textView11.setText(new Boolean(booleanArray[1]).toString());
TextView textView12 = findViewById(R.id.h1_textview);
textView12.setText(new Boolean(booleanArray[2]).toString());
TextView textView13 = findViewById(R.id.textView13);
textView13.setText(new Boolean(booleanArray[3]).toString());
TextView textView14 = findViewById(R.id.textView14);
textView14.setText(new Boolean(booleanArray[4]).toString());
TextView textView15 = findViewById(R.id.textView15);
textView15.setText(new Boolean(booleanArray[5]).toString());
TextView textView16 = findViewById(R.id.textView16);
textView16.setText(new Boolean(booleanArray[6]).toString());
TextView textView17 = findViewById(R.id.textView17);
textView17.setText(new Boolean(booleanArray[7]).toString());
TextView textView18 = findViewById(R.id.textView18);
textView18.setText(new Boolean(booleanArray[8]).toString());
TextView textView19 = findViewById(R.id.textView19);
textView19.setText(new Boolean(booleanArray[9]).toString());
TextView textView20 = findViewById(R.id.textView20);
textView20.setText(new Boolean(booleanArray[10]).toString());
TextView textView21 = findViewById(R.id.textView21);
textView21.setText(new Boolean(booleanArray[11]).toString());
}
};
}

View File

@ -0,0 +1,77 @@
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;
import java.util.HashMap;
public class GlobalState extends Application {
private String ipAddress = "172.16.202.14";
private int port = 502;
private HashMap<Integer, Integer> mapMappingValue = new HashMap<Integer, Integer>();
//
public enum InputConfig {
Q_COIL(8192, 8),
VM_COIL2 (1, 8),
VM_COIL3 (64, 8),
AI_IR(0, 2),
I_DI(0, 12);
private int startReference;
private int count;
InputConfig (int startReference, int count) {
this.startReference = startReference;
this.count = count;
}
// getter/setter
public int getStartReference() {
return startReference;
}
public int getCount() {
return count;
}
}
public HashMap<Integer, Integer> getMapMappingValue() {
initMappingValue();
return mapMappingValue;
}
// 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;
}
private void initMappingValue () {
mapMappingValue.clear();
mapMappingValue.put(InputConfig.VM_COIL3.getStartReference(), InputConfig.Q_COIL.getStartReference()); // 64/8192
mapMappingValue.put(InputConfig.VM_COIL3.getStartReference() + 6, InputConfig.Q_COIL.getStartReference() + 1); // 70/8193
mapMappingValue.put(InputConfig.VM_COIL3.getStartReference() + 1, InputConfig.Q_COIL.getStartReference() + 4); // 65/8196
mapMappingValue.put(InputConfig.VM_COIL3.getStartReference() + 7, InputConfig.Q_COIL.getStartReference() + 5); // 71/8197
mapMappingValue.put(InputConfig.VM_COIL3.getStartReference() + 4, InputConfig.Q_COIL.getStartReference() + 6); // 68/8198
mapMappingValue.put(InputConfig.VM_COIL3.getStartReference() + 5, InputConfig.Q_COIL.getStartReference() + 7); // 69/8199
mapMappingValue.put(InputConfig.VM_COIL3.getStartReference() + 2, InputConfig.Q_COIL.getStartReference() + 2); // 66/8194
mapMappingValue.put(InputConfig.VM_COIL3.getStartReference() + 3, InputConfig.Q_COIL.getStartReference() + 3); // 67/8195
}
}

View File

@ -0,0 +1,268 @@
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.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
public class MainActivity extends AppCompatActivity {
private Handler handlerCoil;
private Handler handlerDiscreteInput;
private GlobalState state;
private static final Integer REFRESH_DELAY = 1000;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
state = (GlobalState) getApplicationContext();
// listener
LocalBroadcastManager.getInstance(this).registerReceiver(coilMessageReceiver, new IntentFilter("readOnlyCoil"));
LocalBroadcastManager.getInstance(this).registerReceiver(discreteInputMessageReceiver, new IntentFilter("readOnlyDiscreteInput"));
// handler
handlerCoil = new Handler();
handlerCoil.post(refreshCoil);
handlerDiscreteInput = new Handler();
handlerDiscreteInput.post(refreshDiscreteInput);
setContentView(R.layout.activity_main);
// swipe event
OnSwipeTouchListener onSwipeTouchListener = new OnSwipeTouchListener(MainActivity.this) {
@Override
public void onSwipeLeft() {
Intent nextIntent = new Intent(MainActivity.this, ManualDriveActivity.class);
startActivity(nextIntent);
// handlerCoil.removeCallbacks(refreshCoil);
// handlerDiscreteInput.removeCallbacks(refreshDiscreteInput);
}
};
final ViewGroup viewGroup = (ViewGroup) ((ViewGroup) this.findViewById(android.R.id.content)).getChildAt(0);
viewGroup.setOnTouchListener(onSwipeTouchListener);
}
@Override
public void onPause () {
super.onPause();
handlerCoil.removeCallbacks(refreshCoil);
handlerDiscreteInput.removeCallbacks(refreshDiscreteInput);
}
@Override
public void onResume () {
super.onResume();
handlerCoil.post(refreshCoil);
handlerDiscreteInput.post(refreshDiscreteInput);
}
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", GlobalState.InputConfig.Q_COIL.getStartReference());
serviceIntent.putExtra("extra.count", GlobalState.InputConfig.Q_COIL.getCount());
serviceIntent.putExtra("extra.intent.name", "readOnlyCoil");
getApplicationContext().startService(serviceIntent);
handlerCoil.postDelayed(this, REFRESH_DELAY);
}
};
Runnable refreshDiscreteInput = new Runnable() {
@Override
public void run() {
Intent serviceIntent = new Intent(getApplicationContext(), MyIntentService.class);
serviceIntent.setAction("read.discrete.input");
serviceIntent.putExtra("extra.ip.address", state.getIpAddress());
serviceIntent.putExtra("extra.ip.port", state.getPort());
serviceIntent.putExtra("extra.ref", GlobalState.InputConfig.I_DI.getStartReference());
serviceIntent.putExtra("extra.count", GlobalState.InputConfig.I_DI.getCount());
serviceIntent.putExtra("extra.intent.name", "readOnlyDiscreteInput");
getApplicationContext().startService(serviceIntent);
handlerDiscreteInput.postDelayed(this, REFRESH_DELAY);
}
};
// on message receive, update the lights with dependings on coil value
private BroadcastReceiver coilMessageReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
boolean[] booleanArray = bundle.getBooleanArray("coilValues");
// ============= Light ===============
ImageView pu1StatusLightOn = findViewById(R.id.pu1_status_light_on_mainview);
ImageView pu1StatusLightOff = findViewById(R.id.pu1_status_light_off_mainview);
ImageView pu2StatusLightOn = findViewById(R.id.pu2_status_light_on_mainview);
ImageView pu2StatusLightOff = findViewById(R.id.pu2_status_light_off_mainview);
ImageView sp1StatusLightOn = findViewById(R.id.sp1_status_light_on_mainview);
ImageView sp1StatusLightOff = findViewById(R.id.sp1_status_light_off_mainview);
ImageView b1StatusLightOn = findViewById(R.id.b1_status_light_on_mainview);
ImageView b1StatusLightOff = findViewById(R.id.b1_status_light_off_mainview);
ImageView m1StatusLightOn = findViewById(R.id.m1_status_light_on_mainview);
ImageView m1StatusLightOff = findViewById(R.id.m1_status_light_off_mainview);
ImageView m2StatusLightOn = findViewById(R.id.m2_status_light_on_mainview);
ImageView m2StatusLightOff = findViewById(R.id.m2_status_light_off_mainview);
// ============== Coil : Range 8192 - 8199 =================
// 8199
if (booleanArray[7]) {
b1StatusLightOn.setVisibility(View.VISIBLE);
b1StatusLightOff.setVisibility(View.INVISIBLE);
} else {
b1StatusLightOn.setVisibility(View.INVISIBLE);
b1StatusLightOff.setVisibility(View.VISIBLE);
}
// 8194
if (booleanArray[2]) {
m1StatusLightOn.setVisibility(View.VISIBLE);
m1StatusLightOff.setVisibility(View.INVISIBLE);
} else {
m1StatusLightOn.setVisibility(View.INVISIBLE);
m1StatusLightOff.setVisibility(View.VISIBLE);
}
// 8195
if (booleanArray[3]) {
m2StatusLightOn.setVisibility(View.VISIBLE);
m2StatusLightOff.setVisibility(View.INVISIBLE);
} else {
m2StatusLightOn.setVisibility(View.INVISIBLE);
m2StatusLightOff.setVisibility(View.VISIBLE);
}
// 8196
if (booleanArray[4]) {
pu1StatusLightOn.setVisibility(View.VISIBLE);
pu1StatusLightOff.setVisibility(View.INVISIBLE);
} else {
pu1StatusLightOn.setVisibility(View.INVISIBLE);
pu1StatusLightOff.setVisibility(View.VISIBLE);
}
// 8197
if (booleanArray[5]) {
pu2StatusLightOn.setVisibility(View.VISIBLE);
pu2StatusLightOff.setVisibility(View.INVISIBLE);
} else {
pu2StatusLightOn.setVisibility(View.INVISIBLE);
pu2StatusLightOff.setVisibility(View.VISIBLE);
}
// 8198
if (booleanArray[6]) {
sp1StatusLightOn.setVisibility(View.VISIBLE);
sp1StatusLightOff.setVisibility(View.INVISIBLE);
} else {
sp1StatusLightOn.setVisibility(View.INVISIBLE);
sp1StatusLightOff.setVisibility(View.VISIBLE);
}
}
};
private BroadcastReceiver discreteInputMessageReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
boolean[] booleanArray = bundle.getBooleanArray("discreteInputValues");
// Lights
ImageView h1StatusLightOn = findViewById(R.id.h1_status_light_on_mainview);
ImageView h1StatusLightOff = findViewById(R.id.h1_status_light_off_mainview);
ImageView h2StatusLightOn = findViewById(R.id.h2_status_light_on_mainview);
ImageView h2StatusLightOff = findViewById(R.id.h2_status_light_off_mainview);
ImageView ind1StatusLightOn = findViewById(R.id.ind1_status_light_on_mainview);
ImageView ind1StatusLightOff = findViewById(R.id.ind1_status_light_off_mainview);
ImageView ls5StatusLightOn = findViewById(R.id.ls5_status_light_on_mainview);
ImageView ls5StatusLightOff = findViewById(R.id.ls5_status_light_off_mainview);
ImageView ls1StatusLightOn = findViewById(R.id.ls1_status_light_on_mainview);
ImageView ls1StatusLightOff = findViewById(R.id.ls1_status_light_off_mainview);
ImageView ls4StatusLightOn = findViewById(R.id.ls4_status_light_on_mainview);
ImageView ls4StatusLightOff = findViewById(R.id.ls4_status_light_off_mainview);
// ============ Discrete Input : Range 0 - 11 ========================
// di 1
if (booleanArray[1]) {
h1StatusLightOff.setVisibility(View.VISIBLE);
} else {
h1StatusLightOff.setVisibility(View.INVISIBLE);
}
// di 2
if (booleanArray[2]) {
h1StatusLightOn.setVisibility(View.VISIBLE);
} else {
h1StatusLightOn.setVisibility(View.INVISIBLE);
}
// di 10
if (booleanArray[10]) {
h2StatusLightOff.setVisibility(View.VISIBLE);
} else {
h2StatusLightOff.setVisibility(View.INVISIBLE);
}
// di 9
if (booleanArray[9]) {
h2StatusLightOn.setVisibility(View.VISIBLE);
} else {
h2StatusLightOn.setVisibility(View.INVISIBLE);
}
// di 11
if (booleanArray[11]) {
ind1StatusLightOff.setVisibility(View.INVISIBLE);
ind1StatusLightOn.setVisibility(View.VISIBLE);
} else {
ind1StatusLightOff.setVisibility(View.VISIBLE);
ind1StatusLightOn.setVisibility(View.INVISIBLE);
}
// di 4
if (booleanArray[4]) {
ls5StatusLightOff.setVisibility(View.INVISIBLE);
ls5StatusLightOn.setVisibility(View.VISIBLE);
} else {
ls5StatusLightOff.setVisibility(View.VISIBLE);
ls5StatusLightOn.setVisibility(View.INVISIBLE);
}
// di 0
if (booleanArray[0]) {
ls1StatusLightOff.setVisibility(View.INVISIBLE);
ls1StatusLightOn.setVisibility(View.VISIBLE);
} else {
ls1StatusLightOff.setVisibility(View.VISIBLE);
ls1StatusLightOn.setVisibility(View.INVISIBLE);
}
// di 3
if (booleanArray[3]) {
ls4StatusLightOff.setVisibility(View.INVISIBLE);
ls4StatusLightOn.setVisibility(View.VISIBLE);
} else {
ls4StatusLightOff.setVisibility(View.VISIBLE);
ls4StatusLightOn.setVisibility(View.INVISIBLE);
}
}
};
}

View File

@ -0,0 +1,390 @@
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.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.ToggleButton;
import java.util.HashMap;
import java.util.Map;
public class ManualDriveActivity extends AppCompatActivity {
private GlobalState state;
private Handler coilHandler;
private Handler manualModeCheckHandler;
private HashMap<Integer, Boolean> mapCoilCheckStatus = new HashMap <> ();
private HashMap<ToggleButton, Integer> mapButtonCoil = new HashMap<>();
private boolean manualMode = false;
private void initMap () {
mapCoilCheckStatus.put(64, false);
mapCoilCheckStatus.put(70, false);
mapCoilCheckStatus.put(65, false);
mapCoilCheckStatus.put(71, false);
mapCoilCheckStatus.put(68, false);
mapCoilCheckStatus.put(69, false);
mapCoilCheckStatus.put(66, false);
mapCoilCheckStatus.put(67, false);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
state = (GlobalState) getApplicationContext();
initMap(); // initialise the value of the coil toggle state + the corresponding coil to button
setContentView(R.layout.activity_manual_drive);
ToggleButton h1ControlButton =findViewById(R.id.h1_control_button);
ToggleButton h2ControlButton = findViewById(R.id.h2_control_button);
ToggleButton pu1ControlButton = findViewById(R.id.pu1_control_button);
ToggleButton pu2ControlButton = findViewById(R.id.pu2_control_button);
ToggleButton sp1ControlButton = findViewById(R.id.sp1_control_button);
ToggleButton b1ControlButton = findViewById(R.id.b1_control_button);
ToggleButton m1ControlButton = findViewById(R.id.m1_control_button);
ToggleButton m2ControlButton = findViewById(R.id.m2_control_button);
//H1 Coil 65-1
mapButtonCoil.put(h1ControlButton, 64);
//H2 Coil 71-1
mapButtonCoil.put(h2ControlButton, 70);
//PU1 Coil 66-1
mapButtonCoil.put(pu1ControlButton, 65);
//PU2 Coil 72-1
mapButtonCoil.put(pu2ControlButton, 71);
//SP1 Coil 69-1
mapButtonCoil.put(sp1ControlButton, 68);
//B1 Coil 70-1
mapButtonCoil.put(b1ControlButton, 69);
//M1 Coil 67-1
mapButtonCoil.put(m1ControlButton, 66);
//M2 Coil 68-1
mapButtonCoil.put(m2ControlButton, 67);
LocalBroadcastManager.getInstance(this).registerReceiver(coilWriterMessageReceiver, new IntentFilter("readVmCoil2"));
// LocalBroadcastManager.getInstance(this).registerReceiver(coilWriterMessageReceiver, new IntentFilter("checkCoilValue"));
LocalBroadcastManager.getInstance(this).registerReceiver(coilMessageReceiver, new IntentFilter("readOnlyCoil"));
// manual drive button
ToggleButton manualDriveOverrideButton = (ToggleButton) findViewById(R.id.manual_override_button);
manualDriveOverrideButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
// writing the value true
Intent serviceIntent = new Intent(getApplicationContext(), MyIntentService.class);
serviceIntent.setAction("write.coil");
serviceIntent.putExtra("extra.ip.address", state.getIpAddress());
serviceIntent.putExtra("extra.ip.port", state.getPort());
serviceIntent.putExtra("extra.ref", GlobalState.InputConfig.VM_COIL2.getStartReference());
serviceIntent.putExtra("extra.bit", true);
serviceIntent.putExtra("extra.intent.name", "readVmCoil2");
serviceIntent.putExtra("extra.is.basic.coil", false);
getApplicationContext().startService(serviceIntent);
} else {
// writing the value false
Intent serviceIntent = new Intent(getApplicationContext(), MyIntentService.class);
serviceIntent.setAction("write.coil");
serviceIntent.putExtra("extra.ip.address", state.getIpAddress());
serviceIntent.putExtra("extra.ip.port", state.getPort());
serviceIntent.putExtra("extra.ref", GlobalState.InputConfig.VM_COIL2.getStartReference());
serviceIntent.putExtra("extra.bit", false);
serviceIntent.putExtra("extra.intent.name", "readVmCoil2");
serviceIntent.putExtra("extra.is.basic.coil", false);
getApplicationContext().startService(serviceIntent);
}
}
});
// for each toggle button, set the event
for (Map.Entry<ToggleButton, Integer> entry : mapButtonCoil.entrySet()) {
final ToggleButton button = entry.getKey();
final Integer coilToWrite = entry.getValue();
button.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked && manualMode) {
// writing the value true
Intent serviceIntent = new Intent(getApplicationContext(), MyIntentService.class);
serviceIntent.setAction("write.coil");
serviceIntent.putExtra("extra.ip.address", state.getIpAddress());
serviceIntent.putExtra("extra.ip.port", state.getPort());
serviceIntent.putExtra("extra.ref", coilToWrite);
serviceIntent.putExtra("extra.bit", true);
serviceIntent.putExtra("extra.intent.name", "writeBit");
serviceIntent.putExtra("extra.is.basic.coil", true);
getApplicationContext().startService(serviceIntent);
mapCoilCheckStatus.put(coilToWrite, true);
} else {
// writing the value false
Intent serviceIntent = new Intent(getApplicationContext(), MyIntentService.class);
serviceIntent.setAction("write.coil");
serviceIntent.putExtra("extra.ip.address", state.getIpAddress());
serviceIntent.putExtra("extra.ip.port", state.getPort());
serviceIntent.putExtra("extra.ref", coilToWrite);
serviceIntent.putExtra("extra.bit", false);
serviceIntent.putExtra("extra.intent.name", "writeBit");
serviceIntent.putExtra("extra.is.basic.coil", true);
getApplicationContext().startService(serviceIntent);
mapCoilCheckStatus.put(coilToWrite, false);
}
}
});
}
}
@Override
protected void onStart () {
super.onStart();
coilHandler = new Handler();
coilHandler.post(refreshCoil);
manualModeCheckHandler = new Handler();
manualModeCheckHandler.post(checkManualMockStatus) ;
}
private BroadcastReceiver coilWriterMessageReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
boolean isManualModeActivated = intent.getBooleanExtra("coil", false);
if (isManualModeActivated) {
manualMode = true;
} else {
manualMode = false;
}
}
};
private Runnable checkManualMockStatus = new Runnable() {
@Override
public void run() {
ToggleButton h1ControlButton = findViewById(R.id.h1_control_button);
ToggleButton h2ControlButton = findViewById(R.id.h2_control_button);
Button pu1ControlButton = findViewById(R.id.pu1_control_button);
Button pu2ControlButton = findViewById(R.id.pu2_control_button);
Button sp1ControlButton = findViewById(R.id.sp1_control_button);
Button b1ControlButton = findViewById(R.id.b1_control_button);
ToggleButton m1ControlButton = findViewById(R.id.m1_control_button);
ToggleButton m2ControlButton = findViewById(R.id.m2_control_button);
// ============= Light ===============
ImageView manualLightOn = findViewById(R.id.manual_override_status_light_on);
ImageView manualLightOff = findViewById(R.id.manual_override_status_light_off);
ImageView h1StatusLightOn = findViewById(R.id.h1_status_light_on);
ImageView h1StatusLightOff = findViewById(R.id.h1_status_light_off);
ImageView h2StatusLightOn = findViewById(R.id.h2_status_light_on);
ImageView h2StatusLightOff = findViewById(R.id.h2_status_light_off);
ImageView pu1StatusLightOn = findViewById(R.id.pu1_status_light_on);
ImageView pu1StatusLightOff = findViewById(R.id.pu1_status_light_off);
ImageView pu2StatusLightOn = findViewById(R.id.pu2_status_light_on);
ImageView pu2StatusLightOff = findViewById(R.id.pu2_status_light_off);
ImageView sp1StatusLightOn = findViewById(R.id.sp1_status_light_on);
ImageView sp1StatusLightOff = findViewById(R.id.sp1_status_light_off);
ImageView b1StatusLightOn = findViewById(R.id.b1_status_light_on);
ImageView b1StatusLightOff = findViewById(R.id.b1_status_light_off);
ImageView m1StatusLightOn = findViewById(R.id.m1_status_light_on);
ImageView m1StatusLightOff = findViewById(R.id.m1_status_light_off);
ImageView m2StatusLightOn = findViewById(R.id.m2_status_light_on);
ImageView m2StatusLightOff = findViewById(R.id.m2_status_light_off);
if (!manualMode) {
manualLightOn.setVisibility(View.INVISIBLE);
manualLightOff.setVisibility(View.VISIBLE);
h1ControlButton.setEnabled(false);
h2ControlButton.setEnabled(false);
m1ControlButton.setEnabled(false);
m2ControlButton.setEnabled(false);
pu1ControlButton.setEnabled(false);
pu2ControlButton.setEnabled(false);
sp1ControlButton.setEnabled(false);
b1ControlButton.setEnabled(false);
manualLightOn.setVisibility(View.INVISIBLE);
manualLightOff.setVisibility(View.VISIBLE);
// switch all off
h1StatusLightOn.setVisibility(View.INVISIBLE);
h1StatusLightOff.setVisibility(View.INVISIBLE);
h2StatusLightOn.setVisibility(View.INVISIBLE);
h2StatusLightOff.setVisibility(View.INVISIBLE);
m1StatusLightOn.setVisibility(View.INVISIBLE);
m1StatusLightOff.setVisibility(View.INVISIBLE);
m2StatusLightOn.setVisibility(View.INVISIBLE);
m2StatusLightOff.setVisibility(View.INVISIBLE);
pu1StatusLightOn.setVisibility(View.INVISIBLE);
pu1StatusLightOff.setVisibility(View.INVISIBLE);
pu2StatusLightOn.setVisibility(View.INVISIBLE);
pu2StatusLightOff.setVisibility(View.INVISIBLE);
sp1StatusLightOn.setVisibility(View.INVISIBLE);
sp1StatusLightOff.setVisibility(View.INVISIBLE);
b1StatusLightOn.setVisibility(View.INVISIBLE);
b1StatusLightOff.setVisibility(View.INVISIBLE);
}
manualModeCheckHandler.postDelayed(this, 1000);
}
};
private 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", GlobalState.InputConfig.Q_COIL.getStartReference());
serviceIntent.putExtra("extra.count", GlobalState.InputConfig.Q_COIL.getCount());
serviceIntent.putExtra("extra.intent.name", "readOnlyCoil");
getApplicationContext().startService(serviceIntent);
coilHandler.postDelayed(this, 3000);
}
};
// on message receive, update the view with values
private BroadcastReceiver coilMessageReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Bundle bundle = intent.getExtras();
boolean[] booleanArray = bundle.getBooleanArray("coilValues");
// ========== Button ============
ToggleButton h1ControlButton = findViewById(R.id.h1_control_button);
ToggleButton h2ControlButton = findViewById(R.id.h2_control_button);
Button pu1ControlButton = findViewById(R.id.pu1_control_button);
Button pu2ControlButton = findViewById(R.id.pu2_control_button);
Button sp1ControlButton = findViewById(R.id.sp1_control_button);
Button b1ControlButton = findViewById(R.id.b1_control_button);
ToggleButton m1ControlButton = findViewById(R.id.m1_control_button);
ToggleButton m2ControlButton = findViewById(R.id.m2_control_button);
// ============= Light ===============
ImageView manualLightOn = findViewById(R.id.manual_override_status_light_on);
ImageView manualLightOff = findViewById(R.id.manual_override_status_light_off);
ImageView h1StatusLightOn = findViewById(R.id.h1_status_light_on);
ImageView h1StatusLightOff = findViewById(R.id.h1_status_light_off);
ImageView h2StatusLightOn = findViewById(R.id.h2_status_light_on);
ImageView h2StatusLightOff = findViewById(R.id.h2_status_light_off);
ImageView pu1StatusLightOn = findViewById(R.id.pu1_status_light_on);
ImageView pu1StatusLightOff = findViewById(R.id.pu1_status_light_off);
ImageView pu2StatusLightOn = findViewById(R.id.pu2_status_light_on);
ImageView pu2StatusLightOff = findViewById(R.id.pu2_status_light_off);
ImageView sp1StatusLightOn = findViewById(R.id.sp1_status_light_on);
ImageView sp1StatusLightOff = findViewById(R.id.sp1_status_light_off);
ImageView b1StatusLightOn = findViewById(R.id.b1_status_light_on);
ImageView b1StatusLightOff = findViewById(R.id.b1_status_light_off);
ImageView m1StatusLightOn = findViewById(R.id.m1_status_light_on);
ImageView m1StatusLightOff = findViewById(R.id.m1_status_light_off);
ImageView m2StatusLightOn = findViewById(R.id.m2_status_light_on);
ImageView m2StatusLightOff = findViewById(R.id.m2_status_light_off);
if (manualMode) {
h1ControlButton.setEnabled(true);
h2ControlButton.setEnabled(true);
m1ControlButton.setEnabled(true);
m2ControlButton.setEnabled(true);
pu1ControlButton.setEnabled(true);
pu2ControlButton.setEnabled(true);
sp1ControlButton.setEnabled(true);
b1ControlButton.setEnabled(true);
manualLightOn.setVisibility(View.VISIBLE);
manualLightOff.setVisibility(View.INVISIBLE);
// lightning
// ======== h1 =========
if (booleanArray[0] == true) {
h1StatusLightOn.setVisibility(View.VISIBLE);
h1StatusLightOff.setVisibility(View.INVISIBLE);
} else {
h1StatusLightOn.setVisibility(View.INVISIBLE);
h1StatusLightOff.setVisibility(View.VISIBLE);
}
// ======== h2 ======
if (booleanArray[1] == true) {
h2StatusLightOn.setVisibility(View.VISIBLE);
h2StatusLightOff.setVisibility(View.INVISIBLE);
} else {
h2StatusLightOn.setVisibility(View.INVISIBLE);
h2StatusLightOff.setVisibility(View.VISIBLE);
}
// ======== m1 ======
if (booleanArray[2] == true) {
m1StatusLightOn.setVisibility(View.VISIBLE);
m1StatusLightOff.setVisibility(View.INVISIBLE);
} else {
m1StatusLightOn.setVisibility(View.INVISIBLE);
m1StatusLightOff.setVisibility(View.VISIBLE);
}
// ======== m2 ======
if (booleanArray[3] == true) {
m2StatusLightOn.setVisibility(View.VISIBLE);
m2StatusLightOff.setVisibility(View.INVISIBLE);
} else {
m2StatusLightOn.setVisibility(View.INVISIBLE);
m2StatusLightOff.setVisibility(View.VISIBLE);
}
// ======== pu1 ======
if (booleanArray[4] == true) {
pu1StatusLightOn.setVisibility(View.VISIBLE);
pu1StatusLightOff.setVisibility(View.INVISIBLE);
} else {
pu1StatusLightOn.setVisibility(View.INVISIBLE);
pu1StatusLightOff.setVisibility(View.VISIBLE);
}
// ======== pu2 ======
if (booleanArray[5] == true) {
pu2StatusLightOn.setVisibility(View.VISIBLE);
pu2StatusLightOff.setVisibility(View.INVISIBLE);
} else {
pu2StatusLightOn.setVisibility(View.INVISIBLE);
pu2StatusLightOff.setVisibility(View.VISIBLE);
}
// ======== m2 ======
if (booleanArray[6] == true) {
sp1StatusLightOn.setVisibility(View.VISIBLE);
sp1StatusLightOff.setVisibility(View.INVISIBLE);
} else {
sp1StatusLightOn.setVisibility(View.INVISIBLE);
sp1StatusLightOff.setVisibility(View.VISIBLE);
}
// ======== m2 ======
if (booleanArray[7] == true) {
b1StatusLightOn.setVisibility(View.VISIBLE);
b1StatusLightOff.setVisibility(View.INVISIBLE);
} else {
b1StatusLightOn.setVisibility(View.INVISIBLE);
b1StatusLightOff.setVisibility(View.VISIBLE);
}
}
}
};
}

View File

@ -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;
}
}

View File

@ -0,0 +1,204 @@
package com.example.user.myapp;
import android.app.IntentService;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import com.ghgande.j2mod.modbus.facade.ModbusTCPMaster;
import com.ghgande.j2mod.modbus.procimg.InputRegister;
import com.ghgande.j2mod.modbus.util.BitVector;
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 {
// read action
private static final String ACTION_READ_COIL = "read.coil";
private static final String ACTION_READ_INPUT_REGISTER = "read.input.register";
private static final String ACTION_READ_DISCRETE_INPUT = "read.discrete.input";
// write action
private static final String ACTION_WRITE_COIL = "write.coil";
private static final String ACTION_CHECK_COIL = "check.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_BIT_TO_SET = "extra.bit";
//turns out modbus on siemens logo is crap and isnt responding for at least 50ms
private static final Integer CAP_WAIT_TIME = 100;
//this is not a type, idiot.
public MyIntentService() {
super("MyIntentService");
}
private void sendBooleanListToActivity(List<Boolean> booleanList, String intentName, String bundleName) {
Intent intent = new Intent(intentName);
Bundle bundle = new Bundle();
bundle.putBooleanArray(bundleName, toPrimitiveArray(booleanList));
intent.putExtras(bundle);
sendLocationBroadcast(intent);
}
private void sendIntegerListToActivity (ArrayList<Integer> integerList, String intentName) {
Intent intent = new Intent(intentName);
Bundle bundle = new Bundle();
bundle.putIntegerArrayList("irValues", integerList);
intent.putExtras(bundle);
sendLocationBroadcast(intent);
}
private void sendLocationBroadcast(Intent intent) {
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
public void readDiscreteInputAction (String ipAddress, int port, int ref, int count, String intentReceiveName) {
try {
master = new ModbusTCPMaster(ipAddress, port);
master.connect();
BitVector bv = master.readInputDiscretes(ref, count);
List<Boolean> listBooleanBit = new ArrayList<>();
for (int i = 0; i < count; i++) {
System.out.println(i+" discret input "+bv.getBit(i));
listBooleanBit.add(bv.getBit(i));
}
sendBooleanListToActivity(listBooleanBit, intentReceiveName, "discreteInputValues");
Thread.sleep(CAP_WAIT_TIME);
master.disconnect();
} catch (Exception e) {
System.out.println("Exception in reading discrete input " + e);
}
}
public void readCoilAction (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++) {
System.out.println(i+" coil "+bv.getBit(i));
listBooleanBit.add(bv.getBit(i));
}
sendBooleanListToActivity(listBooleanBit, intentReceiveName, "coilValues");
Thread.sleep(CAP_WAIT_TIME);
master.disconnect();
} catch (Exception e) {
System.out.println("Exception in reading coil " + e);
}
}
public void readInputRegisterAction (String ipAddress, int port, int ref, int count, String intentReceiveName) {
try {
master = new ModbusTCPMaster(ipAddress, port);
master.connect();
InputRegister [] inputRegisters = master.readInputRegisters(ref, count);
ArrayList<Integer> listIntegerInputRegister = new ArrayList<>();
for (InputRegister ir : inputRegisters) {
listIntegerInputRegister.add(ir.getValue());
System.out.println(ir.getValue()+" input register "+Math.random());
}
sendIntegerListToActivity (listIntegerInputRegister, intentReceiveName);
Thread.sleep(CAP_WAIT_TIME);
master.disconnect();
} catch (Exception e) {
System.out.println("Exception in reading input register " + e);
}
}
public void writeCoilAction (String ipAddress, int port, int ref, boolean value, String intentReceiveName, boolean withCheck) {
try {
master = new ModbusTCPMaster(ipAddress, port);
master.connect();
// writing the value
boolean writtenValue = master.writeCoil(ref, value);
if (withCheck) {
System.out.println("I wrote " + writtenValue + " to coil " + ref);
Thread.sleep(CAP_WAIT_TIME);
GlobalState state = (GlobalState) getApplicationContext();
int coilToRead = state.getMapMappingValue().get(ref);
System.out.println("Checking the value of coil "+coilToRead);
boolean currentValue = master.readCoils(coilToRead, 1).getBit(0);
sendNewBitValueToActivity(currentValue, intentReceiveName);
} else {
System.out.println("I switched the manual mode to " + writtenValue);
sendNewBitValueToActivity(writtenValue, intentReceiveName);
}
Thread.sleep(CAP_WAIT_TIME);
master.disconnect();
} catch (Exception e) {
System.out.println("Exception in writing coil " + e);
}
}
private void sendNewBitValueToActivity (boolean newValue, String intentReceive) {
Intent intent = new Intent(intentReceive);
intent.putExtra("coil", newValue);
sendLocationBroadcast(intent);
}
@Override
protected void onHandleIntent(Intent intent) {
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);
// for write
final Boolean value = intent.getBooleanExtra(EXTRA_BIT_TO_SET, false);
final Boolean withCheck = intent.getBooleanExtra("extra.is.basic.coil", false);
switch(action) {
case ACTION_READ_DISCRETE_INPUT:
readDiscreteInputAction (ipAddress, port, ref, count, intentName);
break;
case ACTION_READ_COIL :
readCoilAction(ipAddress, port, ref, count, intentName);
break;
case ACTION_READ_INPUT_REGISTER :
readInputRegisterAction (ipAddress, port,ref, count, intentName);
break;
case ACTION_WRITE_COIL:
writeCoilAction(ipAddress, port, ref, value, intentName, withCheck);
break;
}
}
}
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;
}
}

View File

@ -0,0 +1,113 @@
package com.example.user.myapp;
import android.content.Context;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
public class OnSwipeTouchListener implements OnTouchListener {
private final GestureDetector gestureDetector;
private Context context;
/* (non-Javadoc)
* @see android.view.View.OnTouchListener#onTouch(android.view.View, android.view.MotionEvent)
*/
public boolean onTouch(final View view, final MotionEvent motionEvent) {
return gestureDetector.onTouchEvent(motionEvent);
}
/**
* Gets the gesture detector.
*
* @return the gesture detector
*/
public GestureDetector getGestureDetector() {
return gestureDetector;
}
/**
* Instantiates a new on swipe touch listener.
*
* @param context the context
*/
public OnSwipeTouchListener(Context context) {
super();
this.context = context;
gestureDetector = new GestureDetector(context, new GestureListener());
}
private final class GestureListener extends SimpleOnGestureListener {
private static final int SWIPE_THRESHOLD = 100;
private static final int SWIPE_VELOCITY_THRESHOLD = 100;
/* (non-Javadoc)
* @see android.view.GestureDetector.SimpleOnGestureListener#onDown(android.view.MotionEvent)
*/
@Override
public boolean onDown(MotionEvent e) {
return true;
}
/* (non-Javadoc)
* @see android.view.GestureDetector.SimpleOnGestureListener#onFling(android.view.MotionEvent, android.view.MotionEvent, float, float)
*/
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
boolean result = false;
try {
float diffY = e2.getRawY() - e1.getRawY();
float diffX = e2.getRawX() - e1.getRawX();
if ((Math.abs(diffX) - Math.abs(diffY)) > SWIPE_THRESHOLD) {
if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
if (diffX > 0) {
onSwipeRight();
} else {
onSwipeLeft();
}
}
} else {
if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
if (diffY > 0) {
onSwipeBottom();
} else {
onSwipeTop();
}
}
}
} catch (Exception e) {
}
return result;
}
}
/**
* On swipe right.
*/
public void onSwipeRight() {
}
/**
* On swipe left.
*/
public void onSwipeLeft() {
}
/**
* On swipe top.
*/
public void onSwipeTop() {
}
/**
* On swipe bottom.
*/
public void onSwipeBottom() {
}
}

View 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;
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;
}

View File

@ -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) {
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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());
}
}
}

View File

@ -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;
}

View File

@ -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());
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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];
}
}

View File

@ -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;
}
}

View File

@ -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];
}
}

View File

@ -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;
}
}

View File

@ -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];
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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];
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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 &gt; 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;
}
}

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -0,0 +1,49 @@
/*
* 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 output.
* <p>
* In Modbus terms this represents a
* coil, which is read-write from slave and
* master or device side.<br>
* Therefor implementations have to be carefully
* designed for concurrency.
*
* @author Dieter Wimberger
* @author Steve O'Hara (4NG)
* @version 2.0 (March 2016)
*/
public interface DigitalOut {
/**
* Tests if this <tt>DigitalOut</tt> is set.
* <p>
*
* @return true if set, false otherwise.
*/
boolean isSet();
/**
* Sets the state of this <tt>DigitalOut</tt>.
* <p>
*
* @param b true if to be set, false otherwise.
*/
void set(boolean b);
}

View File

@ -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.procimg;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Vector;
/**
* @author Julie
*
* FIFO -- an abstraction of a Modbus FIFO, as supported by the
* READ FIFO command.
*
* The FIFO class is only intended to be used for testing purposes and does
* not reflect the actual behavior of a FIFO in a real Modbus device. In an
* actual Modbus device, the FIFO is mapped within a fixed address.
* @author Steve O'Hara (4NG)
* @version 2.0 (March 2016)
*/
public class FIFO {
private static final Logger logger = LoggerFactory.getLogger(FIFO.class);
private int address;
private int registerCount;
private Vector<Register> registers;
public FIFO(int address) {
this.address = address;
registerCount = 0;
registers = new Vector<Register>();
}
public synchronized int getRegisterCount() {
return registerCount;
}
public synchronized Register[] getRegisters() {
Register result[] = new Register[registerCount + 1];
result[0] = new SimpleRegister(registerCount);
for (int i = 0; i < registerCount; i++) {
result[i + 1] = registers.get(i);
}
return result;
}
public synchronized void pushRegister(Register register) {
if (registerCount == 31) {
registers.remove(0);
}
else {
registerCount++;
}
registers.add(new SimpleRegister(register.getValue()));
}
public synchronized void resetRegisters() {
registers.removeAllElements();
registerCount = 0;
}
public int getAddress() {
return address;
}
}

View File

@ -0,0 +1,68 @@
/*
* 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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Julie
*
* File -- an abstraction of a Modbus File, as supported by the
* READ FILE RECORD and WRITE FILE RECORD commands.
* @author Steve O'Hara (4NG)
* @version 2.0 (March 2016)
*/
public class File {
private static final Logger logger = LoggerFactory.getLogger(File.class);
private int fileNumber;
private int record_Count;
private Record records[];
public File(int fileNumber, int records) {
this.fileNumber = fileNumber;
record_Count = records;
this.records = new Record[records];
}
public int getFileNumber() {
return fileNumber;
}
public int getRecordCount() {
return record_Count;
}
public Record getRecord(int i) {
if (i < 0 || i >= record_Count) {
throw new IllegalAddressException();
}
return records[i];
}
public File setRecord(int i, Record record) {
if (i < 0 || i >= record_Count) {
throw new IllegalAddressException();
}
records[i] = record;
return this;
}
}

View File

@ -0,0 +1,50 @@
/*
* 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;
/**
* Class implementing an <tt>IllegalAddressException</tt>. This exception is
* thrown when a non-existant spot in the process image was addressed.
* <p>
* Note that this is a runtime exception, as it is similar to the
* <tt>IndexOutOfBoundsException</tt>
*
* @author Dieter Wimberger
* @author Steve O'Hara (4NG)
* @version 2.0 (March 2016)
*/
public class IllegalAddressException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* Constructs a new <tt>IllegalAddressException</tt>.
*/
public IllegalAddressException() {
}
/**
* Constructs a new <tt>IllegalAddressException</tt> with the given message.
*
* @param message a message as <tt>String</tt>.
*/
public IllegalAddressException(String message) {
super(message);
}
}

View File

@ -0,0 +1,60 @@
/*
* 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 an input register.
* <p>
* This register is read only from the slave side.
*
* @author Dieter Wimberger
* @author Steve O'Hara (4NG)
* @version 2.0 (March 2016)
*/
public interface InputRegister {
/**
* Returns the value of this <tt>InputRegister</tt>. The value is stored as
* <tt>int</tt> but should be treated like a 16-bit word.
*
* @return the value as <tt>int</tt>.
*/
int getValue();
/**
* Returns the content of this <tt>Register</tt> as unsigned 16-bit value
* (unsigned short).
*
* @return the content as unsigned short (<tt>int</tt>).
*/
int toUnsignedShort();
/**
* Returns the content of this <tt>Register</tt> as signed 16-bit value
* (short).
*
* @return the content as <tt>short</tt>.
*/
short toShort();
/**
* Returns the content of this <tt>Register</tt> as bytes.
*
* @return a <tt>byte[]</tt> with length 2.
*/
byte[] toBytes();
}

View File

@ -0,0 +1,51 @@
/*
* 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;
import com.ghgande.j2mod.modbus.util.Observable;
/**
* Class implementing an observable digital output.
*
* @author Dieter Wimberger
* @author Steve O'Hara (4NG)
* @version 2.0 (March 2016)
*/
public class ObservableDigitalOut extends Observable implements DigitalOut {
/**
* A boolean holding the state of this digital out.
*/
protected boolean set;
/**
* Determine if the digital output is set.
*
* @return the boolean value of the digital output.
*/
public boolean isSet() {
return set;
}
/**
* Set or clear the digital output. Will notify any registered
* observers.
*/
public void set(boolean b) {
set = b;
notifyObservers("value");
}
}

View File

@ -0,0 +1,69 @@
/*
* 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;
import com.ghgande.j2mod.modbus.util.Observable;
/**
* Class implementing an observable register.
*
* @author Dieter Wimberger
* @author Steve O'Hara (4NG)
* @version 2.0 (March 2016)
*/
public class ObservableRegister extends Observable implements Register {
/**
* The word holding the content of this register.
*/
protected short register;
synchronized public int getValue() {
return register & 0xFFFF;
}
public final int toUnsignedShort() {
return register & 0xFFFF;
}
public final short toShort() {
return register;
}
public synchronized byte[] toBytes() {
return new byte[]{(byte)(register >> 8), (byte)(register & 0xFF)};
}
public final synchronized void setValue(short s) {
register = s;
notifyObservers("value");
}
public final synchronized void setValue(byte[] bytes) {
if (bytes.length < 2) {
throw new IllegalArgumentException();
}
else {
register = (short)(((short)((bytes[0] << 8))) | (((short)(bytes[1])) & 0xFF));
notifyObservers("value");
}
}
public final synchronized void setValue(int v) {
register = (short)v;
notifyObservers("value");
}
}

View File

@ -0,0 +1,228 @@
/*
* 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 process image in an object oriented manner.
* <p>
* The process image is understood as a shared memory area used form
* communication between slave and master or device side.
*
* @author Dieter Wimberger
* @author Steve O'Hara (4NG)
* @version 2.0 (March 2016)
*/
public interface ProcessImage {
/**
* Returns a range of <tt>DigitalOut</tt> instances.
*
* @param offset the start offset.
* @param count the amount of <tt>DigitalOut</tt> from the offset.
*
* @return an array of <tt>DigitalOut</tt> instances.
*
* @throws IllegalAddressException if the range from offset to offset+count is non existant.
*/
DigitalOut[] getDigitalOutRange(int offset, int count) throws IllegalAddressException;
/**
* Returns the <tt>DigitalOut</tt> instance at the given reference.
*
* @param ref the reference.
*
* @return the <tt>DigitalOut</tt> instance at the given address.
*
* @throws IllegalAddressException if the reference is invalid.
*/
DigitalOut getDigitalOut(int ref) throws IllegalAddressException;
/**
* Returns the number of <tt>DigitalOut</tt> instances in this
* <tt>ProcessImage</tt>.
*
* @return the number of digital outs as <tt>int</tt>.
*/
int getDigitalOutCount();
/**
* Returns a range of <tt>DigitalIn</tt> instances.
*
* @param offset the start offset.
* @param count the amount of <tt>DigitalIn</tt> from the offset.
*
* @return an array of <tt>DigitalIn</tt> instances.
*
* @throws IllegalAddressException if the range from offset to offset+count is non existant.
*/
DigitalIn[] getDigitalInRange(int offset, int count) throws IllegalAddressException;
/**
* Returns the <tt>DigitalIn</tt> instance at the given reference.
*
* @param ref the reference.
*
* @return the <tt>DigitalIn</tt> instance at the given address.
*
* @throws IllegalAddressException if the reference is invalid.
*/
DigitalIn getDigitalIn(int ref) throws IllegalAddressException;
/**
* Returns the number of <tt>DigitalIn</tt> instances in this
* <tt>ProcessImage</tt>.
*
* @return the number of digital ins as <tt>int</tt>.
*/
int getDigitalInCount();
/**
* Returns a range of <tt>InputRegister</tt> instances.
*
* @param offset the start offset.
* @param count the amount of <tt>InputRegister</tt> from the offset.
*
* @return an array of <tt>InputRegister</tt> instances.
*
* @throws IllegalAddressException if the range from offset to offset+count is non existant.
*/
InputRegister[] getInputRegisterRange(int offset, int count) throws IllegalAddressException;
/**
* Returns the <tt>InputRegister</tt> instance at the given reference.
*
* @param ref the reference.
*
* @return the <tt>InputRegister</tt> instance at the given address.
*
* @throws IllegalAddressException if the reference is invalid.
*/
InputRegister getInputRegister(int ref) throws IllegalAddressException;
/**
* Returns the number of <tt>InputRegister</tt> instances in this
* <tt>ProcessImage</tt>.
*
* <p>
* This is not the same as the value of the highest addressable register.
*
* @return the number of input registers as <tt>int</tt>.
*/
int getInputRegisterCount();
/**
* Returns a range of <tt>Register</tt> instances.
*
* @param offset the start offset.
* @param count the amount of <tt>Register</tt> from the offset.
*
* @return an array of <tt>Register</tt> instances.
*
* @throws IllegalAddressException if the range from offset to offset+count is non existant.
*/
Register[] getRegisterRange(int offset, int count) throws IllegalAddressException;
/**
* Returns the <tt>Register</tt> instance at the given reference.
* <p>
*
* @param ref the reference.
*
* @return the <tt>Register</tt> instance at the given address.
*
* @throws IllegalAddressException if the reference is invalid.
*/
Register getRegister(int ref) throws IllegalAddressException;
/**
* Returns the number of <tt>Register</tt> instances in this
* <tt>ProcessImage</tt>.
*
* <p>
* This is not the same as the value of the highest addressable register.
*
* @return the number of registers as <tt>int</tt>.
*/
int getRegisterCount();
/**
* Returns the <tt>File</tt> instance at the given reference.
* <p>
*
* @param ref the reference.
*
* @return the <tt>File</tt> instance at the given address.
*
* @throws IllegalAddressException if the reference is invalid.
*/
File getFile(int ref) throws IllegalAddressException;
/**
* Returns the <tt>File</tt> instance having the specified file number.
*
* @param ref The file number for the File object to be returned.
*
* @return the <tt>File</tt> instance having the given number.
*
* @throws IllegalAddressException if a File with the given number does not exist.
*/
File getFileByNumber(int ref) throws IllegalAddressException;
/**
* Returns the number of <tt>File</tt> instances in this
* <tt>ProcessImage</tt>.
*
* <p>
* This is not the same as the value of the highest addressable register.
*
* @return the number of registers as <tt>int</tt>.
*/
int getFileCount();
/**
* Returns the <tt>FIFO</tt> instance in the list of all FIFO objects
* in this ProcessImage.
*
* @param ref the reference.
*
* @return the <tt>File</tt> instance at the given address.
*
* @throws IllegalAddressException if the reference is invalid.
*/
FIFO getFIFO(int ref) throws IllegalAddressException;
/**
* Returns the <tt>FIFO</tt> instance having the specified base address.
*
* @param ref The address for the FIFO object to be returned.
*
* @return the <tt>FIFO</tt> instance having the given base address
*
* @throws IllegalAddressException if a File with the given number does not exist.
*/
FIFO getFIFOByAddress(int ref) throws IllegalAddressException;
/**
* Returns the number of <tt>File</tt> instances in this
* <tt>ProcessImage</tt>.
*
* <p>
* This is not the same as the value of the highest addressable register.
*
* @return the number of registers as <tt>int</tt>.
*/
int getFIFOCount();
}

View File

@ -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.procimg;
/**
* Interface defining the factory methods for
* the process image and it's elements.
*
* @author Dieter Wimberger
* @author Steve O'Hara (4NG)
* @version 2.0 (March 2016)
*/
public interface ProcessImageFactory {
/**
* Returns a new ProcessImageImplementation instance.
*
* @return a ProcessImageImplementation instance.
*/
ProcessImageImplementation createProcessImageImplementation();
/**
* Returns a new DigitalIn instance.
*
* @return a DigitalIn instance.
*/
DigitalIn createDigitalIn();
/**
* Returns a new DigitalIn instance with the given state.
*
* @param state true if set, false otherwise.
*
* @return a DigitalIn instance.
*/
DigitalIn createDigitalIn(boolean state);
/**
* Returns a new DigitalOut instance.
*
* @return a DigitalOut instance.
*/
DigitalOut createDigitalOut();
/**
* Returns a new DigitalOut instance with the
* given state.
*
* @param b true if set, false otherwise.
*
* @return a DigitalOut instance.
*/
DigitalOut createDigitalOut(boolean b);
/**
* Returns a new InputRegister instance.
*
* @return an InputRegister instance.
*/
InputRegister createInputRegister();
/**
* 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.
*/
InputRegister createInputRegister(byte b1, byte b2);
/**
* Creates a new Register instance.
*
* @return a Register instance.
*/
Register createRegister();
/**
* 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.
*/
Register createRegister(byte b1, byte b2);
}

View File

@ -0,0 +1,239 @@
/*
* 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 implementation specific details of the
* <tt>ProcessImage</tt>, adding mechanisms for creating and modifying the
* actual "process image".
*
* @author Dieter Wimberger
* @author Steve O'Hara (4NG)
* @version 2.0 (March 2016)
*/
public interface ProcessImageImplementation extends ProcessImage {
/**
* Defines the set state (i.e. <b>true</b>) of a digital input or output.
*/
byte DIG_TRUE = 1;
/**
* Defines the unset state (i.e. <b>false</b>) of a digital input or output.
*/
byte DIG_FALSE = 0;
/**
* Defines the invalid (unset, neither true nor false) state of a digital
* input or output.
*/
byte DIG_INVALID = -1;
/**
* Sets a new <tt>DigitalOut</tt> instance at the given reference.
*
* @param ref the reference as <tt>int</tt>.
* @param out the new <tt>DigitalOut</tt> instance to be set.
*
* @throws IllegalAddressException if the reference is invalid.
*/
void setDigitalOut(int ref, DigitalOut out) throws IllegalAddressException;
/**
* Adds a new <tt>DigitalOut</tt> instance.
*
* @param out the <tt>DigitalOut</tt> instance to be added.
*/
void addDigitalOut(DigitalOut out);
/**
* Adds a new <tt>DigitalOut</tt> instance at the given reference.
*
* @param ref - the reference for the instance.
* @param out - the <tt>DigitalOut</tt> instance to be added.
*/
void addDigitalOut(int ref, DigitalOut out);
/**
* Removes a given <tt>DigitalOut</tt> instance.
*
* @param out the <tt>DigitalOut</tt> instance to be removed.
*/
void removeDigitalOut(DigitalOut out);
/**
* Sets a new <tt>DigitalIn</tt> instance at the given reference.
*
* @param ref the reference as <tt>int</tt>.
* @param di the new <tt>DigitalIn</tt> instance to be set.
*
* @throws IllegalAddressException if the reference is invalid.
*/
void setDigitalIn(int ref, DigitalIn di) throws IllegalAddressException;
/**
* Adds a new <tt>DigitalIn</tt> instance.
*
* @param di the <tt>DigitalIn</tt> instance to be added.
*/
void addDigitalIn(DigitalIn di);
/**
* Adds a new <tt>DigitalIn</tt> instance at the given reference, possibly
* creating a hole between the last existing reference and the new object.
*
* @param ref - the reference for the new instance.
* @param di the <tt>DigitalIn</tt> instance to be added.
*/
void addDigitalIn(int ref, DigitalIn di);
/**
* Removes a given <tt>DigitalIn</tt> instance.
*
* @param di the <tt>DigitalIn</tt> instance to be removed.
*/
void removeDigitalIn(DigitalIn di);
/**
* Sets a new <tt>InputRegister</tt> instance at the given reference.
*
* @param ref the reference as <tt>int</tt>.
* @param reg the new <tt>InputRegister</tt> instance to be set.
*
* @throws IllegalAddressException if the reference is invalid.
*/
void setInputRegister(int ref, InputRegister reg) throws IllegalAddressException;
/**
* Adds a new <tt>InputRegister</tt> instance.
*
* @param reg the <tt>InputRegister</tt> instance to be added.
*/
void addInputRegister(InputRegister reg);
/**
* Adds a new <tt>InputRegister</tt> instance, possibly
* creating a hole between the last existing reference and the new object.
*
* @param ref - The reference for the new instance.
* @param reg the <tt>InputRegister</tt> instance to be added.
*/
void addInputRegister(int ref, InputRegister reg);
/**
* Removes a given <tt>InputRegister</tt> instance.
*
* @param reg the <tt>InputRegister</tt> instance to be removed.
*/
void removeInputRegister(InputRegister reg);
/**
* Sets a new <tt>Register</tt> instance at the given reference.
*
* @param ref the reference as <tt>int</tt>.
* @param reg the new <tt>Register</tt> instance to be set.
*
* @throws IllegalAddressException if the reference is invalid.
*/
void setRegister(int ref, Register reg) throws IllegalAddressException;
/**
* Adds a new <tt>Register</tt> instance.
*
* @param reg the <tt>Register</tt> instance to be added.
*/
void addRegister(Register reg);
/**
* Adds a new <tt>Register</tt> instance, possibly
* creating a hole between the last existing reference and the new object.
*
* @param ref - the reference for the new instance.
* @param reg the <tt>Register</tt> instance to be added.
*/
void addRegister(int ref, Register reg);
/**
* Removes a given <tt>Register</tt> instance.
*
* @param reg the <tt>Register</tt> instance to be removed.
*/
void removeRegister(Register reg);
/**
* Sets a new <tt>File</tt> instance at the given reference.
*
* @param ref the reference as <tt>int</tt>.
* @param reg the new <tt>File</tt> instance to be set.
*
* @throws IllegalAddressException if the reference is invalid.
*/
void setFile(int ref, File reg) throws IllegalAddressException;
/**
* Adds a new <tt>File</tt> instance.
*
* @param reg the <tt>File</tt> instance to be added.
*/
void addFile(File reg);
/**
* Adds a new <tt>File</tt> instance, possibly
* creating a hole between the last existing reference and the new object.
*
* @param ref - the reference for the new isntance.
* @param reg the <tt>File</tt> instance to be added.
*/
void addFile(int ref, File reg);
/**
* Removes a given <tt>File</tt> instance.
*
* @param reg the <tt>File</tt> instance to be removed.
*/
void removeFile(File reg);
/**
* Sets a new <tt>FIFO</tt> instance at the given reference.
*
* @param ref the reference as <tt>int</tt>.
* @param reg the new <tt>FIFO</tt> instance to be set.
*
* @throws IllegalAddressException if the reference is invalid.
*/
void setFIFO(int ref, FIFO reg) throws IllegalAddressException;
/**
* Adds a new <tt>FIFO</tt> instance.
*
* @param reg the <tt>FIFO</tt> instance to be added.
*/
void addFIFO(FIFO reg);
/**
* Adds a new <tt>FIFO</tt> instance, possibly
* creating a hole between the last existing reference and the new object.
*
* @param ref - the reference for the new instance.
* @param reg the <tt>FIFO</tt> instance to be added.
*/
void addFIFO(int ref, FIFO reg);
/**
* Removes a given <tt>FIFO</tt> instance.
*
* @param reg the <tt>FIFO</tt> instance to be removed.
*/
void removeFIFO(FIFO reg);
}

View File

@ -0,0 +1,72 @@
/*
* 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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Julie
*
* File -- an abstraction of a Modbus Record, as supported by the
* READ FILE RECORD and WRITE FILE RECORD commands.
* @author Steve O'Hara (4NG)
* @version 2.0 (March 2016)
*/
public class Record {
private static final Logger logger = LoggerFactory.getLogger(Record.class);
private int recordNumber;
private int registerCount;
private Register registers[];
public Record(int recordNumber, int registers) {
this.recordNumber = recordNumber;
registerCount = registers;
this.registers = new Register[registers];
for (int i = 0; i < registerCount; i++) {
this.registers[i] = new SimpleRegister(0);
}
}
public int getRecordNumber() {
return recordNumber;
}
public int getRegisterCount() {
return registerCount;
}
public Register getRegister(int register) {
if (register < 0 || register >= registerCount) {
throw new IllegalAddressException();
}
return registers[register];
}
public Record setRegister(int ref, Register register) {
if (ref < 0 || ref >= registerCount) {
throw new IllegalAddressException();
}
registers[ref] = register;
return this;
}
}

Some files were not shown because too many files have changed in this diff Show More