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
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'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.
I have a server side app written in C
struct recv_packet
{
int magic;
int code;
int length;
char *body;
};
char send_buff[1024+1] = "";
ZeroMemory(&send_buff, 1024);
memset(send_buff, 'A', 1024);
//send_buff[1024] = '\0';
recv_packet rcv_pkt = { 0 };
rcv_pkt.magic = MAGIC;
rcv_pkt.code = 0;
rcv_pkt.length = strlen(send_buff);
rcv_pkt.body = send_buff;
int size = sizeof(rcv_pkt.magic) + sizeof(rcv_pkt.code) + sizeof(rcv_pkt.length) + 1024+1;
if (send(ClientSocket, (char *)&rcv_pkt, size, 0) == SOCKET_ERROR)
{
printf("Error %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
On the other side i grab this packet like this:
public struct recv_packet
{
public int magic;
public int code;
public int length;
public byte[] body;
};
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
int port = 4000;
TcpClient client = new TcpClient("127.0.0.1", 4000);
NetworkStream nws = client.GetStream();
BinaryWriter bw = new BinaryWriter(nws);
BinaryReader br = new BinaryReader(nws);
byte[] buff = new byte[512];
send_packet pkt = new send_packet();
pkt.magic = magic;
pkt.cmd = (int)command.MOVE_MOUSE;
while (true)
{
bw.Write(pkt.magic);
bw.Write(pkt.cmd);
//br.Read(buff, 0, 512);
recv_packet rcv_pkt = new recv_packet();
rcv_pkt.magic = br.ReadInt32();
rcv_pkt.code = br.ReadInt32();
rcv_pkt.length = br.ReadInt32();
rcv_pkt.body = br.ReadBytes(rcv_pkt.length);
//string str = rcv_pkt.length.ToString();
string str = System.Text.Encoding.Default.GetString(rcv_pkt.body);
MessageBox.Show(str);
}
}
So it suppose that body will have only '65', but instead I've got some trash in it.
Why could this happen? Thank you for your time.
As I understood there are few ways of resolving this problem - one of them is to redecrlare struct a little bit and then creare a buffer, where all structure elements will be fitted one-by-one. So the solution looks like this:
char send_buff[1024+1] = "";
ZeroMemory(&send_buff, 1025);
memset(send_buff, 'A', 1024);
recv_packet *rcv_pkt = (recv_packet *)malloc(sizeof(recv_packet)+1024+1);
//recv_packet rcv_pkt = { 0 };
rcv_pkt->magic = MAGIC;
rcv_pkt->code = 0;
rcv_pkt->length = strlen(send_buff);
memcpy(rcv_pkt->body, send_buff, 1025);
int size = sizeof(rcv_pkt->magic) + sizeof(rcv_pkt->code) + sizeof(rcv_pkt->length) + 1024 + 1;
//printf("%d", size);
//getchar();
//return 0;
//if (send(ClientSocket, rcv_pkt.body, rcv_pkt.length, 0) == SOCKET_ERROR)
if (send(ClientSocket, (char *)rcv_pkt, size, 0) == SOCKET_ERROR)
i Have question about CRLF after sending a string to the serial port.
Let me explain what im trying to do, Under here there is an example.
[ACR] CRLF
10:90 CRLF
11:20 CRLF
12:2.1 CRLF
That is what im trying to do but i cant get anywhere, Can someone help me with this.
i think i have to do the CRLF to start a newline but if you have other suggestions they will be more then welcome
This is what i've done:
Code:
private void SendMessageTest()
{
var content = _durCleaningTextbox.Text;
byte[] array = ComPort.StringToBytes(content);
_comport.WriteBytes(array);
_comport.ReadBytes(array, array.Length, 1000);
string result = Encoding.ASCII.GetString(array);
MessageBox.Show(result);
}
using System;
using System.IO;
using System.Text;
using System.IO.Ports;
namespace Communication
{
public class ComPort
{
private readonly SerialPort _serialPort;
public ComPort(string portName, int baudRate)
{
_serialPort = new SerialPort();
_serialPort.PortName = portName;
_serialPort.BaudRate = baudRate;
_serialPort.StopBits = StopBits.One;
_serialPort.DataBits = 8;
_serialPort.Parity = Parity.None;
_serialPort.Handshake = Handshake.None;
// _serialPort.WriteBufferSize = 1;
_serialPort.DtrEnable = true;
_serialPort.RtsEnable = true;
_serialPort.Open();
_serialPort.ReadTimeout = 20000;
_serialPort.WriteTimeout = 20000;
}
public void Clear()
{
while (ReadByte() != -1)
continue;
}
private byte[] _array = new byte[] {0};
public void WriteByte(byte value)
{
_array[0] = value;
_serialPort.Write(_array, 0, 1);
// _serialPort.BaseStream.WriteByte(value);
_serialPort.BaseStream.Flush();
}
public void WriteBytes(byte[] array)
{
_serialPort.Write(array, 0, array.Length);
}
public void WriteBytes(byte[] array, int index, int length )
{
_serialPort.Write(array, index, length);
}
private int _readTimeOut = -1;
public int ReadByte(int timeOut = 200)
{
if (timeOut != _readTimeOut)
_serialPort.ReadTimeout = _readTimeOut = timeOut;
try
{
//return _serialPort.BaseStream.ReadByte();
return _serialPort.ReadByte();
// _serialPort.Read(array, 0, 1);
// return array[0];
}
catch (TimeoutException)
{
return -1;
}
}
public int ReadBytes(byte[] array, int length, int timeOut = 200)
{
if (timeOut != _readTimeOut)
_serialPort.ReadTimeout = _readTimeOut = timeOut;
try
{
//return _serialPort.BaseStream.ReadByte();
int bytesRead = 0;
while ( bytesRead < length )
bytesRead += _serialPort.Read(array, bytesRead, length - bytesRead);
// _serialPort.Read(array, 0, 1);
// return array[0];
return bytesRead;
}
catch (TimeoutException)
{
return -1;
}
}
/// <summary>
/// sends string followed by CR - LF
/// </summary>
/// <param name="line"></param>
public void WriteLine(String line)
{
WriteBytes(StringToBytes(line + "\r\n"));
}
public static byte[] StringToBytes(string input)
{
return Encoding.ASCII.GetBytes(input);
}
public void Close()
{
try
{
_serialPort.DtrEnable = false;
_serialPort.RtsEnable = false;
_serialPort.Close();
}
catch(IOException)
{
}
}
public bool Dtr
{
get { return _serialPort.DtrEnable; }
set { _serialPort.DtrEnable = value; }
}
public bool Rts
{
get { return _serialPort.RtsEnable; }
set { _serialPort.RtsEnable = value; }
}
}
}
byte[] array = ComPort.StringToBytes(content + "\r\n");
Or use your WriteLine method which already does this. So:
byte[] array = ComPort.StringToBytes(content);
_comport.WriteBytes(array);
Becomes:
_comport.WriteLine(content);
You need send the commands CR (Carriage Return) and LF (Line Feed, or new line).
For this is just to send your command plus the the CR and LF like this:
string command = "myCommand";
port.write(string.format("{0}\r\n", command));
\r\n = CR + LF -> Used as a new line character in Windows
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.