I'm working on a c# project Which have to comunicate with PLC by TCP Modbus.
I'm using Nmodbus Library and it works fine.
The problem is when I try to read/write Registry over 12000.
I get this exception:
Exception 'Modbus.SlaveException' Function Code: 131
This is the part of the code which generates the exception:
private TcpClient tcpClient;
private ModbusIpMaster master;
private void connect(){
// connect to Modbus TCP Server
string ipAddress = "192.168.77.7"; //Input WISE IP
int tcpPort = 502;
tcpClient = new TcpClient(ipAddress, tcpPort);
// create Modbus TCP Master by the tcp client
master = ModbusIpMaster.CreateIp(tcpClient);
// rewrite the value of AO ch0 (40020~40021) by float
byte slaveID = 1;
// ushort rewriteAddress = 20;
// ushort[] rewriteValue = new ushort[2] { 0, 0 };
// float[] floatData = new float[1] { 223.456F };
// Buffer.BlockCopy(floatData, 0, rewriteValue, 0, 4);
Random random = new Random();
// read the holding register 12001~12005
// write the holding register 301~305
ushort startAddress = 12000;
ushort numOfPoints = 5;
master.Transport.ReadTimeout = 1000;
while (!_shouldStop1)
{
try
{
ushort[] register = master.ReadHoldingRegisters(slaveID, startAddress, numOfPoints);
for (int index = 0; index <register.Length; index++)
{
Console.WriteLine(string.Format("AO[{0}] = {1}", index,register[index]));
}
for (ushort index = 0; index < 4; index++)
{
master.WriteSingleRegister(slaveID, (ushort)(301 + index),(ushort) data[index]);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Any suggestion will be appreciated.
Thanks,
Federico.
Related
I am Sending 68bytes of data using UDP Protocol.
68bytes of data consist of 4byte of int and random 64byte of byte array. uint seq starts with zero and it will increase if client send datagram to server once at a time. I used BitConverter.GetBytes for data's seq to make byte array.
public class NewData
{
public uint seq;
public byte[] data = new byte[64];
public NewData()
{
seq = 0;
data = null;
}
public NewData(uint seq, byte[] data)
{
this.seq = seq;
this.data = data;
}
}
Server has 2 Threads. 1 Thread will Enqueue and the other thread will Dequeue. (Producer and Consumer Thread)
I tried to check the data is coming well.
private readonly ConcurrentQueue<NewData> queue = new ConcurrentQueue<NewData>();
private void ReceiveThread()
{
int recv;
uint seq = 0;
byte[] datagram = new byte[1024];
List<byte> list = new List<byte>(); // for enqueue seq test
while (true)
{
autoresetevent.WaitOne();
if (Dispatcher.Invoke(() => (string)StartButton.Content == "Stop"))
{
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.Bind(endpoint);
socket.Blocking = false;
IPEndPoint sender = new IPEndPoint(IPAddress.Any, Dispatcher.Invoke(() => Convert.ToInt32(portTextBox.Text)));
EndPoint tmpRemote = (EndPoint)sender;
while (true)
{
try
{
recv = socket.ReceiveFrom(datagram, ref tmpRemote);
}
catch (SocketException e)
{
Thread.Sleep(threadSleep);
continue;
}
////------------------------------------------------------------------------
//// To Test Datagram Sequence
////------------------------------------------------------------------------
//for (int i = 0; i < 4; i++)
//{
// list.Add(datagram[i]);
//}
//Debug.Write(Convert.ToString(BitConverter.ToUInt32(list.ToArray(), 0)) + " ");
//list.Clear();
NewData newdata = new NewData(seq, datagram);
queue.Enqueue(newdata);
////------------------------------------------------------------------------
//// To check queue Count. if, queue Count = Client packet sent, no packet lost
////------------------------------------------------------------------------
//Debug.Write(Convert.ToString(queue.Count()) + " ");
seq++;
if (Dispatcher.Invoke(() => (string)StartButton.Content == "Start"))
{
socket.Close();
break;
}
Thread.Sleep(threadSleep);
}
}
}
}
private void FileSaveThread()
{
uint packet_lost = 0;
uint oldValue = 0;
uint currentValue = 0;
int j = 0; // for index
List<byte> sequenceList = new List<byte>(); // sequenceList
while (true)
{
autoresetevent2.WaitOne()
NewData newdata = new NewData();
if (queue.TryDequeue(out newdata))
{
for (j = 0; j < 4; j++)
sequenceList.Add(newdata.data[j]);
oldValue = BitConverter.ToUInt32(sequenceList.ToArray(), 0); // oldValue에 현재값 저장
queue.TryPeek(out newdata);
for (j = 0; j < 4; j++)
sequenceList.Add(newdata.data[j]);
currentValue = BitConverter.ToUInt32(sequenceList.ToArray(), 0); // oldValue에 현재값 저장
//Debug.Write(Convert.ToString(currentValue) + " ");
sequenceList.Clear();
if (!(currentValue == oldValue + 1))
{
packet_lost++;
Dispatcher.Invoke(() => dataResultTextBox.Text += " Packet_Lost : " + packet_lost + "\n");
}
}
Thread.Sleep(threadSleep);
}
}
The datagram's seq missing after 973.
Debug.Write () says
... 970 971 972 974 977 981 984 987 991 994 998 1001 1004 1007 1010 1014 1017 1021 1023 1027 1030 1034 1037 ...
Why does interval changed since the datagram sequence increased 1 at a time?
Or Should I think about other way to change byte array to Int?
Edit) I am sending data per 10ms. It works when i send data per 100ms.
client code
private async void DataSender(int num, int cycle, string ip, int port)
{
uint dataSequence = 0;
byte[] data = new byte[64]; // 64byte data
byte[] byteDataSeq = new byte[4]; // int sequence to byte
byte[] datagram = new byte[1024]; // seq + data
List<byte> datagramList = new List<byte>();
IPEndPoint ep = new IPEndPoint(IPAddress.Parse(ip), port); // 서버의 주소 지정
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); // udp 소켓 client 선언
while (true)
{
if ((string)startButton.Content == "Start")
{
break;
}
random.NextBytes(data);
byteDataSeq = BitConverter.GetBytes(dataSequence);
datagramList.AddRange(byteDataSeq);
datagramList.AddRange(data);
datagram = datagramList.ToArray();
datagramList.Clear();
client.SendTo(datagram, ep);
dataSequence++;
await Task.Delay(cycle);
}
}
If Client sends data and sleep for 10ms,
Dequeue Thread should not sleep more than 10ms. It should be more faster than sender.
For example, If you send data per 5ms, transaction per second will be 200 data. Then your Dequeue Thread should not sleep. Even 1 ms sleep will cause error.
Debug.Write will cause error too. If you try to print every data that socket received, Dequeue thread won't work properly.
I am communicating with a device via UDP, I send a message and I receive the response.
public byte[] Rx_message = new byte[12];
public byte[] packedMessage2 = new byte[12];
public IPEndPoint sendEndPoint;
public void senderUdpClient(byte Type, byte CommandC, byte CodeCmd, Int32 X, Int32 Y)
{
string serverIP = "192.168.2.11";
int sendPort = 40960;
int receivePort = 40961;
var span = new Span<byte>(packedMessage2);
span[0] = Type;
span[1] = CommandC;
span[2] = CodeCmd;
BinaryPrimitives.WriteInt32LittleEndian(span.Slice(3, 4), X);
BinaryPrimitives.WriteInt32LittleEndian(span.Slice(7, 4), Y);
var sum = unchecked((byte)packedMessage2.Take(11).Sum(x => x));
span[11] = sum;
sendEndPoint = new IPEndPoint(IPAddress.Parse(serverIP), sendPort);
try
{
UdpClient senderClient = new UdpClient();
senderClient.Connect(this.sendEndPoint);
senderClient.Send(packedMessage2, packedMessage2.Length);
IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
UdpClient receivingUdpClient = new UdpClient(receivePort);
receivingUdpClient.Client.ReceiveTimeout = 50;
// receive message
Rx_message = receivingUdpClient.Receive(ref RemoteIpEndPoint);
senderClient.Close();
receivingUdpClient.Close();
}
catch (Exception ex)
{
}
}
I want to add a retry mechanism if the equipment does not respond in the event of a network problem,
(if I have an error: Rx_message [0] = -1 and if all goes well Rx_message [0] = 2)
Normally I should do 2 retry if I can connect I continue, otherwise I display an error message
public byte[] Rx_message = new byte[12];
public byte[] packedMessage2 = new byte[12];
public IPEndPoint sendEndPoint;
public int Connected = 0;
public void senderUdpClient(byte Type, byte CommandC, byte CodeCmd, Int32 X, Int32 Y)
{
string serverIP = "192.168.2.11";
int sendPort = 40960;
int receivePort = 40961;
var span = new Span<byte>(packedMessage2);
span[0] = Type;
span[1] = CommandC;
span[2] = CodeCmd;
BinaryPrimitives.WriteInt32LittleEndian(span.Slice(3, 4), X);
BinaryPrimitives.WriteInt32LittleEndian(span.Slice(7, 4), Y);
var sum = unchecked((byte)packedMessage2.Take(11).Sum(x => x));
span[11] = sum;
sendEndPoint = new IPEndPoint(IPAddress.Parse(serverIP), sendPort);
try
{
for(int i = 0; i< 2; i++)
{
UdpClient senderClient = new UdpClient();
senderClient.Connect(this.sendEndPoint);
senderClient.Send(packedMessage2, packedMessage2.Length);
IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
UdpClient receivingUdpClient = new UdpClient(receivePort);
receivingUdpClient.Client.ReceiveTimeout = 50;
// receive message
Rx_message = receivingUdpClient.Receive(ref RemoteIpEndPoint);
//first case where it is connected and I receive my answer correctly
if (Rx_message[0] == 2)
{
Connected = 1;
break;
}
//if I can't connect
if (Connected == 0)
{
//first try
if (i < 1)
{
Thread.Sleep(20);
continue;
}
//second try
if (i == 1)
{
//these two lines I put it just so that the code goes to try and shows me the error code
receivingUdpClient.Client.ReceiveTimeout = 1500;
Rx_message = receivingUdpClient.Receive(ref RemoteIpEndPoint);
}
}
senderClient.Close();
receivingUdpClient.Close();
}
}
catch (Exception ex)
{
if (Connected == 0)
{
MessageBox.Show("Error Connection");
}
sbyte type_message = Convert.ToSByte(Rx_message[0]);
if (type_message == -1)
{
if (Rx_message[3] == 1)
{
MessageBox.Show("Type invalide");
}
if (Rx_message[3] == 2)
{
MessageBox.Show("Commande invalide");
}
if (Rx_message[3] == 3)
{
MessageBox.Show("Argument invalide!");
}
if (Rx_message[3] == 4)
{
MessageBox.Show("Erreur CS!");
}
}
}
}
Here is the code I tried to do, once it gets to the reading line
Rx_message = receivingUdpClient.Receive(ref RemoteIpEndPoint);
he goes directly to Catch I don't know how I can force him to continue the code, otherwise someone can help me improve this mechanism
I am looking for a little help.
I have a program to communicate with a controller through Modbus TCP.
The only problem is I cannot extend the Nop from 125 to 400 because I got Illegal Data Address error message.
Could you please help me with this?
try
{
byte slaveid = 1;
byte function = 4;
ushort id = function;
ushort startAddress = 0;
uint NoP = 125;
byte[] frame = ReadInputRegistersMsg(id, slaveid, startAddress, function, NoP);
this.Write(frame); //data send to controller
Thread.Sleep(100);
byte[] buffReceiver = this.Read(); //data recieving from controller
int SizeByte = buffReceiver[8]; // Data what I got from the controller
UInt16[] temp = null;
if (function != buffReceiver[7])
{
byte[] byteMsg = new byte[9];
Array.Copy(buffReceiver, 0, byteMsg, 0, byteMsg.Length);
byte[] data = new byte[SizeByte];
textBox2.Text = Display(byteMsg);
byte[] errorbytes = new byte[3];
Array.Copy(buffReceiver, 6, errorbytes, 0, errorbytes.Length);
this.CheckValidate(errorbytes); // check the answer -> error message
}
else
{
byte[] byteMsg = new byte[9 + SizeByte];
Array.Copy(buffReceiver, 0, byteMsg, 0, byteMsg.Length);
byte[] data = new byte[SizeByte];
textBox2.Text = Display(byteMsg); // Show received messages in windows form app
Array.Copy(buffReceiver, 9, data, 0, data.Length);
temp = Word.ConvertByteArrayToWordArray(data); // Convert Byte[]-> Word[]
}
// Result
if (temp == null) return;
string result = string.Empty;
//foreach (var item in temp) // show all the data
for(int i=0;i<100;i++) // show the first 100 data
{
//result += string.Format("{0} ", item);
result += temp[i];
}
textBox3.Text = result; // insert the result into the textbox (windows form app)
}
catch
{
}
The ReadInputRegister Message is the following:
private byte[] ReadInputRegistersMsg(ushort id, byte slaveAddress, ushort startAddress, byte function, uint NoP)
{
byte[] frame = new byte[12];
frame[0] = (byte)(id >> 8); // Transaction Identifier High
frame[1] = (byte)id; // Transaction Identifier Low
frame[2] = 0; // Protocol Identifier High
frame[3] = 0; // Protocol Identifier Low
frame[4] = 0; // Message Length High
frame[5] = 6; // Message Length Low(6 bytes to follow)
frame[6] = slaveAddress; // Slave address(Unit Identifier)
frame[7] = function; // Function
frame[8] = (byte)(startAddress >> 8); // Starting Address High
frame[9] = (byte)startAddress; // Starting Address Low
frame[10] = (byte)(NoP >> 8); // Quantity of Registers High
frame[11] = (byte)NoP; // Quantity of Registers Low
return frame;
}
The hard limit for modbus is 125 NoP
I have some Python code for capturing images from a camera and sending them to a C# server.
When sending the messages from the client- I precede the data with the message size so I know how much data to pull from the socket server-side.
It seems to work well most of the time, but occasionally - the message doesn't appear to start with the message size.
I'm not sure why this is happening but I can't figure out how to deal with it.
Python code:
while True:
send_message("SEND_FRAME_DATA_HERE")
def send_message(message):
message_size = len(message.encode())
print (f"Message: {message_size} - {message}")
my_socket.sendall(struct.pack(">L", message_size) + message.encode())
C#
private const int MESSAGE_CHUNK_SIZE = 4096;
private const int MESSAGE_PREFIX_SIZE = 4;
private void _receiveMessage(IAsyncResult ar)
{
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;
List<byte> messageBuffer = new List<byte>();
byte[] tempBuffer = new byte[MESSAGE_CHUNK_SIZE];
try
{
handler.EndReceive(ar);
messageBuffer.AddRange(state.messageBuffer);
while (true)
{
while (messageBuffer.Count < MESSAGE_PREFIX_SIZE)
{
handler.Receive(tempBuffer, 0, MESSAGE_CHUNK_SIZE, 0);
messageBuffer.AddRange(tempBuffer);
}
int messageLength = _getMessageLength(messageBuffer);
// Occasionally the four bytes determining message length
// are read from what appears to be mid message
if (messageLength > 20)
{
Console.Write("halp");
}
messageBuffer = messageBuffer.Skip(MESSAGE_PREFIX_SIZE).ToList();
while (messageBuffer.Count < messageLength)
{
handler.Receive(tempBuffer, 0, StateObject.messageChunkSize, 0);
messageBuffer.AddRange(tempBuffer);
}
var wholeMessage = messageBuffer.Take(messageLength).ToList();
var messageString = Encoding.Default.GetString(wholeMessage.ToArray());
Console.WriteLine(messageString);
messageBuffer = messageBuffer.Skip(messageLength).ToList();
}
}
catch (SocketException ex)
{
Console.WriteLine(ex.Message);
}
}
private int _getMessageLength(List<byte> message)
{
byte[] bytes = { message[3], message[2], message[1], message[0] };
return BitConverter.ToInt32(bytes, 0);
}
The message buffer should look something like this:
On a good run:
On a bad run:
The problem appears to be with this code:
handler.Receive(tempBuffer, 0, StateObject.messageChunkSize, 0);
messageBuffer.AddRange(tempBuffer);
Socket.Receive() returns the number of bytes actually read into the tempBuffer. You need to save that value, and then use it to copy the correct number of bytes to messageBuffer.
int bytesRead = handler.Receive(tempBuffer, 0, StateObject.messageChunkSize, 0);
messageBuffer.AddRange(tempBuffer.Take(bytesRead));
I am a newbie to modbus and need some help. I am trying to connect using modbus and serial communication. so far i managed to send data but i am unable to get any. the following is my code.
Building packet
private byte[] BuildPacket(int meter_address,int function,int table_name,int table_offset,int high_byte, int low_byte)
{
try
{
byte[] packet = new byte[6];
packet[0] = Convert.ToByte(meter_address);
packet[1] = Convert.ToByte(function);
packet[2] = Convert.ToByte(table_name);
packet[3] = Convert.ToByte(table_offset);
packet[4] = Convert.ToByte(high_byte);
packet[5] = Convert.ToByte(low_byte);
byte[] checksum = DoCheckSum(packet);
byte[] sendPacket = new byte[8];
sendPacket[0] = packet[0];
sendPacket[1] = packet[1];
sendPacket[2] = packet[2];
sendPacket[3] = packet[3];
sendPacket[4] = packet[4];
sendPacket[5] = packet[5];
sendPacket[6] = checksum[0];
sendPacket[7] = checksum[1];
return sendPacket;
}
catch (Exception)
{
throw;
}
}
Checksum for modbus
try
{
ushort CRCFull = 0xFFFF;
byte CRCHigh = 0xFF, CRCLow = 0xFF;
char CRCLSB;
for (int i = 0; i < (packet.Length); i++)
{
CRCFull = (ushort)(CRCFull ^ packet[i]);
for (int j = 0; j < 8; j++)
{
CRCLSB = (char)(CRCFull & 0x0001);
CRCFull = (ushort)((CRCFull >> 1) & 0x7FFF);
if (CRCLSB == 1)
CRCFull = (ushort)(CRCFull ^ 0xA001);
}
}
byte[] crcByte = new byte[2];
crcByte[1] = CRCHigh = (byte)((CRCFull >> 8) & 0xFF);
crcByte[0] = CRCLow = (byte)(CRCFull & 0xFF);
return crcByte;
}
catch (Exception ex)
{
throw ex;
}
}
Connection through serial and modbus
public void ConnectSerialModBus(string COM, int baud)
{
SerialPort port = new SerialPort(COM, baud, Parity.None, 8, StopBits.One);
if (!(port.IsOpen))
{
byte[] sendPacket = BuildPacket(3, 4, 11, 0, 1, 200);
port.Open();
port.RtsEnable = false;
port.Handshake = Handshake.None;
//SEND PACKET TO DEVICE
port.Write(sendPacket, 0, sendPacket.Length);
#region RECEIVE DATA FROM SERIAL
//MAKE PROCESS STOP FOR 5sec
Thread.Sleep(3000);
port.DiscardOutBuffer();
port.DiscardInBuffer();
port.RtsEnable = true;
int size = port.ReadBufferSize;
byte[] readingbyte = new byte[size];
port.Read(readingbyte, 0, readingbyte.Length);
string reading = Encoding.GetEncoding("Windows-1252").GetString(readingbyte);
port.Close();
port.Dispose();
#endregion
}
}
The problem is when it comes to reading the response, the program gets stuck. if possible please help me out figure what is wrong with it.
found a solution to the problem, the problem was with the thread.sleep. was giving it 3secs which is too much for the rtf to receive the packet. changed to 10ms and worked fine.