I was able to make a connection with a PLC to read data from it. Now there's one problem and it is that I have to write a method to modify the data from the PLC. To achieve this, I have to send two values to the PLC: an int value and a boolean value. I got the int value solved via the classes from the net.wimpi.modbus package. But when it comes to the boolean value I have no clue what to do.
If someone had the same problem as I do now, could you please send me a reference where I can find a solution or a link of a really good tutorial to solve my problem? Someone posted a couple of links in this question but it sends me to tutorials that doesn't have much to do with the communication with the PLC's and how to treat the data of the PLC.
EDIT
I made the connection with a Modicon M340 PLC, and for the connection I use the classes of the net.wimpi.modbus package. I made the connection in my code through the classes ModbusTCPTransaction and TCPMasterConnection, and I read the values through the classes ReadMultipleRegistersRequest and ReadMultipleRegistersResponse.
The code I made for the connection:
private InetAddress m_Address;
private ModbusTCPTransaction m_Transaction = null;
private TCPMasterConnection m_Connection = null;
int port = Modbus.DEFAULT_PORT;
private Activity activity;
public ModbusConnection(Activity activity, String ip)
{
this.activity = activity;
try {
m_Address = InetAddress.getByName(ip);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} // the slave'saddress
}
public void getTransaction(InetAddress inet) throws Exception
{
/*Variables for the reading of the PLC data*/
int port = Modbus.DEFAULT_PORT;
/*Data initialization for the reading of the PLC data*/
m_Connection = new TCPMasterConnection(inet);
m_Connection.setPort(port);
m_Connection.connect();
m_Transaction = new ModbusTCPTransaction(m_Connection);
}
And to read the values, I call the next code all the time. I accomplished only reading and writing int, String and float values through words that I read from an offset declared on the PLC:
private ReadMultipleRegistersResponse readValues(int offset, int count)
{
ReadMultipleRegistersRequest rReq = null; // the request
ReadMultipleRegistersResponse rRes = null; // the response
try {
rReq = new ReadMultipleRegistersRequest(offset, count);
m_Transaction.setRequest(rReq);
m_Transaction.execute();
rRes = (ReadMultipleRegistersResponse) m_Transaction.getResponse();
} catch (Exception e) {
e.printStackTrace();
Log.i("AsyncTask", "doInBackground: Exception");
}
return rRes;
}
EDIT 2
I think I accomplished what I wanted. There are 4 classes that I use to read the coils:
ReadCoilsRequest
ReadCoilsResponse
WriteMultipleCoilsRequest
WriteMultileCoilsResponse
What I did is two methods to read and write coils into the PLC:
private ReadCoilsResponse readBytes(int offset, int count)
{
ReadCoilsRequest rReq = null; // the request
ReadCoilsResponse rRes = null; // the response
try {
rReq = new ReadCoilsRequest(offset, count);
m_Transaction.setRequest(rReq);
m_Transaction.execute();
rRes = (ReadCoilsResponse) m_Transaction.getResponse();
} catch (Exception e) {
e.printStackTrace();
Log.i("AsyncTask", "doInBackground: Exception");
}
return rRes;
}
public void writeBytes(int wordNumber, BitVector b) {
try {
WriteMultipleCoilsRequest wReq = null; //
WriteMultipleCoilsResponse wRes = null; //
wReq = new WriteMultipleCoilsRequest(211, b);
m_Transaction.setRequest(wReq);
m_Transaction.execute();
} catch (Exception e) {
e.printStackTrace();
Log.i("AsyncTask", "doInBackground: Exception");
}
}
Also, I made a method to read BitVector variables using the Coils classes:
public BitVector readBitVector(int offset, int count)
{
BitVector myBitVector;
ReadCoilsResponse rRes = readBytes(offset, count);
rRes = (ReadCoilsResponse) m_Transaction.getResponse();
myBitVector = rRes.getCoils();
return myBitVector;
}
After this, what I used to set the bit to 1 or 0 is using a native function from the BitVector class from the net.wimpi.modbus.util package in my code:
test.setBit(2, true);
NOTE: It is important to remember that everytime that you want to read or write values to the plc, the best way to do it is opening a closing the connection to the PLC.
My definitive answer is: you have to treat the registers as bits. So if you want to write the second bit of a register represented in an int value in your code, you will have to do something like this:
intValue = m_Data[1].getValue();
intValue = intValue | 2;
m_Data[1].setValue(intValue);
The second line modifies the bit I want to write in my PLC.
Related
I am using sockets and I have one c++ server and one java client.
My c++ server is waiting for a c++ struct like this one:
struct Testing{
double a;
double b;
double c;
};
And my server code looks like this:
int recvStruct(int fd)
{
struct Testing test;
int ibytes = sizeof(struct Testing);
if (ibytes!=read(fd,&test,ibytes))
{
printf("Error read\r\n");
return 0;
}
else
{
dosomething();
Then in my java client, i created one class:
public class Testing{
double a,b,c;
public Testing(double a, double b, double c){
this.a = a;
this.b = b;
this.c = c;
}
}
and for the last, my client java code is this one:
try {
Socket socket = new Socket("ipaddress", 12345);
OutputStream os= socket.getOutputStream();
Testing object = new Object(1,2,3);
os.writeObject(object);
socket.close();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
/* e.printStackTrace();
response = "UnknownHostException: " + e.toString();*/
} catch (IOException e) {
// TODO Auto-generated catch block
/*e.printStackTrace();
response = "IOException: " + e.toString();*/
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
/* e.printStackTrace();*/
}
}
My problem is that i always get that error read message.
Thank you.
Your code just assumes that your C++ Testing class consists of the exact same number of bytes, in the exact same order, as the result of Java's OutputStream.writeObject function.
OutputStream.writeObject isn't even a function to serialise the component bytes of its members; it does much more than that:
The class of the object, the signature of the class, and the values of the non-transient and non-static fields of the class and all of its supertypes are written.
In conclusion, given that there is no relationship between the two whatsoever, success is extremely unlikely.
Instead, serialise to your own known format, then deserialise into the language structure you require. Research the bolded terms for more information.
If your struct is a sequence of 3 double, assuming in C++ a double is 8 byte as in Java, you should send a sequence of 24 bytes.
To convert each double I would use:
long bits = Double.doubleToLongBits(someDouble);
byte[] bytes = new byte[8];
for(int offset = 0, shift = 7*8;
offset < bytes.length;
offset++, shift-=8)
bytes[offset] = (byte)(bits >>> shift);
I have a Client-Server system where server is written in cpp and the client is written is Java (Android application).
The server reads an image from a local directory as an ifstream using read method.
The reading process is done inside a loop, where the program reads parts of the image every time. Every time a part of the image is read, it's sent over a socket to the client that collects all the piece inside a byteBuffer and when all the bytes of the image are transfered to the client, the client attempts to turn that array of bytes (after using byteBuffer.array() method) into a Bitmap.
This is where the problem begins - I've tried a few methods but it seems that I'm unable to turn this array of bytes into a Bitmap.
From what I understood, this byte array is probably a raw representation of the image, which can't be decodded using methods like BitmapFactory.decodeByteArray() since it wasn't encoded in the first place.
Ultimately, my question is - how can I proccess this array of bytes so that I'll be able to set the image as a source to an ImageView?
Note: I've already made sure that all the data is sent over the socket correctly and the pieces are collected in the right order.
Client code:
byte[] image_bytes
byte[] response_bytes;
private void receive_image ( final String protocol, final int image_size, final int buffer_size)
{
if (image_size <= 0 || buffer_size <= 0)
return;
Thread image_receiver = new Thread(new Runnable() {
#Override
public void run() {
ByteBuffer byteBuffer = ByteBuffer.allocate(image_size);
byte[] buffer = new byte[buffer_size];
int bytesReadSum = 0;
try {
while (bytesReadSum != image_size) {
activeReader.read(buffer);
String message = new String(buffer);
if (TextUtils.substring(message, 0, 5len_of_protocol_number).equals(protocol)) {
int bytesToRead = Integer.parseInt(TextUtils.substring(message,
len_of_protocol_number,
len_of_protocol_number + len_of_data_len));
byteBuffer.put(Arrays.copyOfRange(buffer,
len_of_protocol_number + len_of_data_len,
bytesToRead + len_of_protocol_number + len_of_data_len));
bytesReadSum += bytesToRead;
} else {
response_bytes = null;
break;
}
}
if (bytesReadSum == image_size) {
image_bytes = byteBuffer.array();
if (image_bytes.length > 0)
response_bytes = image_bytes;
else
response_bytes = null;
}
} catch (IOException e) {
response_bytes = null;
}
}
});
image_receiver.start();
try {
image_receiver.join();
} catch (InterruptedException e) {
response_bytes = null;
}
if (response_bytes != null)
{
final ImageView imageIV = (ImageView) findViewById(R.id.imageIV);
File image_file = new File(Environment.getExternalStorageDirectory(), "image_file_jpg");
try
{
FileOutputStream stream = new FileOutputStream(image_file);
stream.write(image_bytes);
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
//Here the method returns null
final Bitmap image_bitmap = BitmapFactory.decodeFile(image_file.getAbsolutePath());
main.this.runOnUiThread(new Runnable() {
#Override
public void run() {
imageIV.setImageBitmap(image_bitmap);
imageIV.invalidate();
}
}
}
}
Whenever you exchange data between two machines of different architectures via sockets you need to know the Endianness (big-endian/little-endian) of each machine. If different, you will need to convert bytes to correct the data. Perhaps that's your issue. Here's a link with sample code: Converting Little Endian to Big Endian. You should be able to easily find more articles explaining the concept.
It turned out that something was wrong with my sending protocol.
After patching it up a bit it actually worked.
Thanks for the help.
I'm trying to send a parcel through a socket to an android application. The client is in libbinder(c++) and the server is an android application that will have to rebuild the parcel. I have been looking for a solution for some time but I don't know how to serialize the parcel and then re-build it on the server side. Any ideas on how this can be done?
Thanks
The part of code where I handle the data
Client
Parcel parc = Parcel();
double three = 5.5;
parc.writeDouble(three);
unsigned char b[sizeof(parc)];
std::memcpy(b, &parc, sizeof(parc));
Then I send like this
send(client, b, sizeof(b), 0);
Server
private int count
private InputStream in = null;
try {
in = socket.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
try {
count = in.read(bytes);
}catch (IOException e) {
e.printStackTrace();
}
Parcel parcel = Parcel.obtain();
parcel.unmarshall(bytes, 0, bytes.length);
parcel.setDataPosition(0);
double d = parcel.readDouble();
Log.v("----double---", "double" + d);
A good example can be found here.
In general, you will need to make sure that you have the classes available to reconstruct (create from parcel) the objects.
I've a class which is responsible for listening two other machines which have exactly the same classes, so it's a network of three computers having the same code. The connection is there and I can see them passing data to each other. Everything until there works OK.
Things get tricky when I take out one of the machines and observe how the other two behave. Expectedly, when one of the machines stops working for some reason, other two should continue. And if two of them stop, the remaining should go on.
I tried to implement this mechanism below. However, when I take out one of the machines, the program keeps waiting, so it does not switch to "two-way comparison mode".
public void listen() {
try {
logger.info("Creating listener sockets");
while (isRunning) {
final byte[] buf = new byte[bufferSize];
final DatagramPacket packetOne = new DatagramPacket(buf, buf.length);
final DatagramPacket packetTwo = new DatagramPacket(buf, buf.length);
MediatorMessageMsg mediatorMessageOne = null;
MediatorMessageMsg mediatorMessageTwo = null;
try {
socketReceiverOne.receive(packetOne);
ByteArrayInputStream firstInput = new ByteArrayInputStream(buf);
mediatorMessageOne = MediatorMessageMsg.parseDelimitedFrom(firstInput);
socketReceiverTwo.receive(packetTwo);
ByteArrayInputStream secondInput = new ByteArrayInputStream(buf);
mediatorMessageTwo = MediatorMessageMsg.parseDelimitedFrom(secondInput);
logger.trace("Received packets");
} catch (final SocketTimeoutException e) {
logger.trace(e.getMessage());
continue;
} catch (final SocketException e) {
logger.warn(e);
logger.warn("Ignore the error and go on.");
continue;
} catch (final IOException e) {
logger.error("Incoming communication stopped!");
logger.error(e);
stop();
}
// if two mediators sent the data, it's OK
if (packetOne.getLength() > 0 && packetTwo.getLength() > 0) {
handlePackets(mediatorMessageOne, mediatorMessageTwo);
logger.info("Number of active mediators: 2. Comparison style: 1v1v1");
}
// if only one sent the data, compare it with our own
else if (packetOne.getLength() > 0 || packetTwo.getLength() > 0) {
// whicehever sent the data, compare its data with our own
logger.info("Number of active mediators: 1. Comparison style: 1v1");
if (packetOne.getLength() > 0) {
handlePackets(mediatorMessageOne);
} else {
handlePackets(mediatorMessageTwo);
}
}
// if no data is sent, then pass our own directly
else {
logger.info("Number of active mediators: 0. Comparison style: No Comparison");
// our datamodel to retrieve data on our own
DataModel modelOwn = DataModel.getInstance();
MediatorMessageMsg newMessage = MediatorMessageMsg.newBuilder().setHeading(modelOwn.getHeading()).setSpeed(modelOwn.getSpeed()).setSender(getId()).build();
// publish(topicName, newMessage);
}
Thread.sleep(1);
}
socketReceiverOne.close();
socketReceiverTwo.close();
logger.info("stopped");
} catch (final IllegalArgumentException e) {
logger.error("Illegal argument received: " + e);
} catch (final Exception e) {
logger.error("Unexpected error occured: " + e);
} finally {
if (socketReceiverOne instanceof DatagramSocket && socketReceiverTwo instanceof DatagramSocket) {
if (!socketReceiverOne.isClosed() || !socketReceiverTwo.isClosed()) {
socketReceiverOne.close();
socketReceiverTwo.close();
}
}
}
}
To save your time, let me share my opinion on the matter. I suspect the problem to be in this part:
socketReceiverOne.receive(packetOne);
ByteArrayInputStream firstInput = new ByteArrayInputStream(buf);
mediatorMessageOne = MediatorMessageMsg.parseDelimitedFrom(firstInput);
socketReceiverTwo.receive(packetTwo);
ByteArrayInputStream secondInput = new ByteArrayInputStream(buf);
mediatorMessageTwo = MediatorMessageMsg.parseDelimitedFrom(secondInput);
To me it seems like the program expects a package and when it cannot receive it, it keeps waiting. Although I have time out exception condition, I cannot get this done.
private int socketTimeout = 1000 * 2;// 2sec
socketReceiverOne.setSoTimeout(socketTimeout);
socketReceiverTwo.setSoTimeout(socketTimeout);
Any thoughts?
Okay I found where I was mistaken. I needed more ports (for in and out). Once I incorporated those ports, the problem did not occur again.
I have looked at many examples and tried to understand what i`m doing wrong but with no success, maybe you can help me. It always stops at the second file, but the first one is just crated on c:\ with 0kb size.
files_server.get(i) is ArrayList with all files that i wish to download.
My code:
public FTPConnection() {
StartD std = new StartD();
std.start();
}
class StartD extends Thread{
#Override
public void run()
{
for (int i = 0; i < files_server.size(); i++) {
err = ftpDownload(files_server.get(i), "C:/"+ files_server.get(i));
if (!err)
{
System.out.println("Error in download, breaking");
break;
}
}
}
public boolean ftpDownload(String srcFilePath, String desFilePath)
{
try {
FileOutputStream desFileStream = new FileOutputStream(desFilePath);
InputStream input = mFTPClient.retrieveFileStream(srcFilePath);
byte[] data = new byte[1024];
int count;
while ((count = input.read(data)) != -1)
{
desFileStream.write(data, 0, count);
}
desFileStream.close();
} catch (Exception e) {
return false;
}
return true;
}}
If I use the finction:
public boolean ftpDownload(String srcFilePath, String desFilePath) {
boolean status = false;
try {
FileOutputStream desFileStream = new FileOutputStream(desFilePath);
status = mFTPClient.retrieveFile(srcFilePath, desFileStream);
desFileStream.close();
return status;
} catch (Exception e) {
}
return status;
}
instead, everything works just fine, but i can`t monitor file download progress.
I've only used it for file unzipping and not FTP, but in that case InputStream buffers can return zero, so I'd say it's worth trying changing your while loop to something like:
while ((count = input.read(data)) >= 0)
public int read(byte[] b) throws IOException
Reads some number of bytes from the input stream and stores them into the buffer array b.
The number of bytes actually read is returned as an integer. This
method blocks until input data is available, end of file is detected,
or an exception is thrown.
If the length of b is zero, then no bytes are read and 0 is returned;
It could also be that you're assigning count twice, which could chop the first byte off the data:
int count = input.read(data);
while ((count = input.read(data)) != -1)
So don't assign anything to count when you declare it.
Let's assume your library is the FTP client from the commons-net package. It's not easy to figure out what's wrong with your code, because we can't run it and because your description (the second file stops) is not sufficient (does it throw an exception? Does it hang forever? Does it complete without any side effect?). Anyway I have a couple of advices:
Use a CountingOutputStream (from Apache commons-io) to monitor progress
Use a ProtocolCommandListener to log what's going on
Also, note that the first 1024 bytes are always lost. Eventually, I don't know how safe it is to put a file in C:\ with the same name it has on the server. At the best, it could lead to permission troubles, at the worst it may originate a security flaw - anyway this doesn't hold if you have some degree of control over the filenames, but hey consider this advice.
This is a sample client
public class FTP {
public static void main(String[] args) throws SocketException, IOException {
FTPClient client = new FTPClient();
client.addProtocolCommandListener(new ProtocolCommandListener(){
#Override
public void protocolCommandSent(ProtocolCommandEvent evt) {
logger.debug(evt.getMessage());
}
#Override
public void protocolReplyReceived(ProtocolCommandEvent evt) {
logger.debug(evt.getMessage());
}
});
client.connect("ftp.mozilla.org");
client.login("anonymous", "");
client.enterLocalPassiveMode();
OutputStream out = new CountingOutputStream(new NullOutputStream()) {
#Override
public void beforeWrite(int count) {
super.beforeWrite(count);
logger.info("Downloaded " + getCount() + " bytes");
}
};
for (String filename: new String[] {"MD5SUMS", "SHA1SUMS"})
client.retrieveFile("pub/firefox/releases/15.0b4/" + filename, out);
out.close();
client.disconnect();
}
private static Logger logger;
static {
logger = Logger.getLogger(FTP.class.getCanonicalName());
}
}
Once configured, the logger will output all the raw socket conversation, and it may help you to better understand the problem, provided it's on the FTP side and not in application IO