I am trying to access addresses such as 50526 on a Socomec Diris A40 using nmodbus. Unlike other examples I have seen which start with 3 or 4, these addresses all start with a 5. 50544, 50550, 50556 are but a few more im intersted in.
As far as I understand it at the moment, the first number represents the Modbus function and does not actually refer to the real address, i.e. 30000 addresses use function 04, 40000 addresses function 03 (?). I have seen the first digit omitted and the rest used as the address. If I try this with my 50000 addresses I get some success, but not with all values and the results dont seem correct. MODPOLL returns the same results as my code.
I could really use some help! If anyone can advise me on how to access these 5xxxx registers, I would be extremely grateful.
Method code:
public static void ModbusSerialRtuMasterReadRegisters()
{
using (SerialPort port = new SerialPort("COM1"))
{
// configure serial port
port.BaudRate = 9600;
port.DataBits = 8;
port.Parity = Parity.None;
port.StopBits = StopBits.One;
try
{
port.Open();
Console.WriteLine("port " + port.PortName + " open: " + port.IsOpen + "\n");
}
catch(Exception ex)
{
Console.WriteLine("Unable to open port: " + ex);
}
// create modbus master
IModbusSerialMaster master = ModbusSerialMaster.CreateRtu(port);
byte slaveId = 1;
ushort startAddress = 1;
ushort numRegisters = 5;
ushort[] registers = new ushort[numRegisters];;
// read registers
try
{
registers = master.ReadHoldingRegisters(slaveId, startAddress, numRegisters);
for (int i = 0; i < numRegisters; i++)
Console.WriteLine("Register {0}={1}", startAddress + i, registers[i]);
}
catch (Modbus.SlaveException se)
{
Console.WriteLine("Could not find register... \n \n" + se);
}
try
{
port.Close();
Console.WriteLine("\nport " + port.PortName + " open: " + port.IsOpen + "\n");
}
catch (Exception ex)
{
Console.WriteLine("Unable to close port: " + ex);
}
}
Try to subtract 40001 or 40000 from 5xxxx address (addresses start with 1 or 0).
Registers with 5xxxx address are holding registers. (40001 to 5xxxx range)
So to find Modbus register address you should subtract its address from 40001.
for example 50512 - 40001=10511 (290F H)
good luck
Related
i'm programming serial port communication with hardware by our supplier. It's trivial, but I cannot get the right data from the port. I need to write to serial port 2 bytes of address, then receive 1 byte confirmation. HW supplier has testing software, which works exactly like that, but when I write the same bytes, then what I get from it is different.
How should it look like:
Write: 0x00 0x01
Read: 0xAA
How I get results:
Write: 0x00 0x01
Read: 0x3F 0x3F 0xAA - sometimes just 0x3F one time
I'm using SerialPort class in C#. Testing software from HW supplier is written is Pascal(synaser library) - I do not have source code. It seems like C# is doing something more behind, but I cannot find the issue. For data analysis I used Free Device Monitoring Studio to analyze exact data on the bus.
private bool SendAddress()
{
int confirmation;
byte[] address = { (byte)((Address >> 8) & 0xFFu), Address };
var confirmed = false;
var port = new SerialPort(Port, 19200, Parity.Mark, 8);
port.WriteTimeout = 200;
port.ReadTimeout = 200;
StatusReaderService.PortLoggers[Port].Debug(Name + " Opening port for address.");
port.Open();
StatusReaderService.PortLoggers[Port].Debug(Name + " Port opened.");
StatusReaderService.PortLoggers[Port].Debug(Name + " Discarding buffers.");
port.DiscardOutBuffer();
port.DiscardInBuffer();
StatusReaderService.PortLoggers[Port].Debug(Name + " Buffers discarded.");
StatusReaderService.PortLoggers[Port].Debug(Name + " Writing address: " + BitConverter.ToString(address));
//port.Write(address, 0, address.Length);
port.BaseStream.WriteByte(0x00);
port.BaseStream.WriteByte(0x01);
StatusReaderService.PortLoggers[Port].Debug(Name + " Address written.");
try
{
for (int i = 0; i < 3; i++)
{
StatusReaderService.PortLoggers[Port].Debug(Name + " Waiting for confirmation.");
confirmation = port.ReadByte();
StatusReaderService.PortLoggers[Port].Debug(Name + " Confirmation value received: " + BitConverter.ToString(BitConverter.GetBytes(confirmation), 0, 1));
if (confirmation == 0xAA)
{
StatusReaderService.PortLoggers[Port].Debug(Name + " Confirmed.");
confirmed = true;
break;
}
else
{
StatusReaderService.PortLoggers[Port].Debug(Name + " Invalid confirmation value.");
}
}
} catch(TimeoutException)
{
port.Close();
throw;
}
StatusReaderService.PortLoggers[Port].Debug(Name + " Closing port.");
port.Close();
Console.ReadKey();
if (confirmed == false)
{
StatusReaderService.PortLoggers[Port].Debug(Name + " Confirmation not received.");
return confirmed;
}
return confirmed;
}
I'm looping through the result, but thats not the way we want to do it. Didn't someone experience similar problem?
Thanks everyone for help. kunif revealed the problem.
HW supplier told me that it's receiving address with parity set to 1, but then sends confirmation without parity. It's suppliers problem as it should return data with same parity. So SerialPort class replaced errors with it's defined 0x3F value. In suppliers SW, where Pascal is used, there is no parity check.
Didn't notice that since C# didn't throw any exception and it's my first real experience with serial port.
One more time thanks all :)
I have built a C# console app that accepts TCP connections from GPS reporting devices I have. I built this app to collect that data and dump it into a SQL Server table.
Currently, I have the application working, but it has a bug I can't seem to figure out. As the GPS devices make connections, one out of random 1-10 successful connections give me an index out of range exception.
When I dump the raw data it does not look like something the device is sending me. Would any of you happen to know what is causing this? Also, once I get this application working correctly, it could be receiving up to 3-5k connections a minute, do you think this code could handle this?
This is the error I receive every so often, with the dump of misc data:
Image of error
This is my code:
namespace GPS2DB
{
class Program
{
static void Main(string[] args)
{
try
{
IPAddress ipAddress = IPAddress.Parse("10.71.150.253");
Console.WriteLine("Waiting for Tracker Connections...");
TcpListener listener = new TcpListener(ipAddress, 10000);
listener.Start();
while (true)
{
Socket client = listener.AcceptSocket();
Console.WriteLine("Connection accepted.");
var childSocketThread = new Thread(() =>
{
byte[] data = new byte[1024];
int size = client.Receive(data);
string gpsData = "";
for (int i = 0; i < size; i++)
{
Console.Write(Convert.ToChar(data[i]));
gpsData = gpsData + Convert.ToChar(data[i]);
}
string txt = gpsData;
string txt2 = (txt.Trim(new Char[] { '$', '#' }));
String[] values = txt2.Split(',');
//Console.WriteLine(txt2);
/*
Console.WriteLine("Unit ID: " + values[0]);
Console.WriteLine("Event Code: " + values[1]);
Console.WriteLine("UTC Date: " + values[2]);
Console.WriteLine("UTC Time: " + values[3]);
Console.WriteLine("Lat: " + values[4]);
Console.WriteLine("Long: " + values[5]);
Console.WriteLine("Speed: " + values[7]);
Console.WriteLine("Heading: " + values[11]);
Console.WriteLine("V+: " + values[16]);
Console.WriteLine("Cell Strength: " + values[17]);
Console.WriteLine("GPS Status: " + values[18]);
Console.WriteLine("Fuel Level: " + values[20]);
*/
//dump 2 database
string connectionString = "Data Source=DVT501;Initial Catalog=VehicleTracking;Persist Security Info=True;User ID=TABLE;Password=PASS";
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand cmd = new SqlCommand("INSERT INTO Data_Dump (uid, eventCode, utcDate, utcTime, lat, long, speed, heading, voltage, cellStrength, gpsStatus, fuelLevel) VALUES (#uid, #eventCode, #utcDate, #utcTime, #lat, #long, #speed, #heading, #voltage, #cellStrength, #gpsStatus, #fuelLevel)");
cmd.CommandType = System.Data.CommandType.Text;
cmd.Connection = connection;
try
{
cmd.Parameters.AddWithValue("#uid", values[0]);
cmd.Parameters.AddWithValue("#eventCode", values[1]);
cmd.Parameters.AddWithValue("#utcDate", values[2]);
cmd.Parameters.AddWithValue("#utcTime", values[3]);
cmd.Parameters.AddWithValue("#lat", values[4]);
cmd.Parameters.AddWithValue("#long", values[5]);
cmd.Parameters.AddWithValue("#speed", values[7]);
cmd.Parameters.AddWithValue("#heading", values[11]);
cmd.Parameters.AddWithValue("#voltage", values[16]);
cmd.Parameters.AddWithValue("#cellStrength", values[17]);
cmd.Parameters.AddWithValue("#gpsStatus", values[18]);
cmd.Parameters.AddWithValue("#fuelLevel", values[20]);
connection.Open();
cmd.ExecuteNonQuery();
}
catch (System.IndexOutOfRangeException e)
{
Console.WriteLine("IndexOutOfRangeException caught" + e);
Console.WriteLine(txt);
}
}
//end dump
Console.WriteLine();
client.Close();
});
childSocketThread.Start();
}
listener.Stop();
}
catch (Exception e)
{
Console.WriteLine("Error: " + e.StackTrace);
Console.ReadLine();
}
}
}
}
The error is with the incoming data, it's either a gps device that's configured to send data differently or some random tcp event. Check the port number you're using in a google search and make sure it's not reserved for something else.
This code will definitely not handle that many connections, you iterate through a byte array and convert one character at a time (use System.Text.Encoding.ASCII.GetString(byte[]) instead), you open and close a connection to the sql server within the receive block and so on. In order to handle that kind of activity you need to just read the data and put it in a bus or temp storage to be bulk processed.
You are assuming that you will read one message at a time. TCP provides a boundaryless stream of bytes. You can very well read a partial message or multiple messages.
How to deal with that depends on the format of the stream. If it is line based StreamReader.ReadLine() is a great solution.
The following method calls Ping.Send(). When I pass an invalid URL, Send() dies and an unhandled exception happens. What is the cause of this?
private void ping()
{
comboBox3.Visible = false;
listBox2.Items.Clear();
// check the url if it is null
if (string.IsNullOrEmpty(textBox1.Text) || textBox1.Text == "")
{
listBox2.Items.Add("Please use valid IP or web address!!");
comboBox3.Visible = false;
coloring_red_tab4();
}
else
{
// do the ping
coloring_green_tab4();
for (int i = 0; i < numericUpDown1.Value; i++)
{
string s;
s = textBox1.Text;
string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
byte[] buffer = Encoding.ASCII.GetBytes(data);
int timeout = 120;
Ping p = new Ping();
PingOptions options = new PingOptions();
options.DontFragment = true;
//pingexception was unhalded (if the url wrong here is the error)
PingReply r = p.Send(s, timeout, buffer, options);
// if it's true url
if (r.Status == IPStatus.Success)
{
listBox2.Items.Add("Ping to " + s.ToString() + "[" + r.Address.ToString() + "]" + " (Successful) "
+ "Bytes =" + r.Buffer.Length + " TTL=" + r.Options.Ttl + " Response delay = " + r.RoundtripTime.ToString() + " ms " + "\n");
label91.Text = r.Address.ToString();
}
else
{
// just to know the ip for the website if they block the icmp protocol
listBox2.Items.Add(r.Status);
IPAddress[] ips;
ips = Dns.GetHostAddresses(textBox1.Text);
foreach (IPAddress ip in ips)
{
label91.Text = ip.ToString();
}
}
}
}
}
The exception is unhandled because you do not handle it. Whenever you call a .Net library method, you need to check its documentation to see what exceptions it throws, and decide which, if any, you want to handle at that level of code. Here is the relevant portion of the documentation for Ping.Send(), which I am including as an image so you will be able to recognize these sections going forward:
Notice that the documentation states that a PingException can occur if
An exception was thrown while sending or receiving the ICMP messages. See the inner exception for the exact exception that was thrown.
Thus it's clear from the documentation that many errors from Ping() will be reported as thrown exceptions rather than reported by setting PingReply.Status != IPStatus.Success. So you need to modify your code to be something like the following:
public static bool TryPing(string hostNameOrAddress, out string pingStatusMessage, out string pingAddressMessage)
{
if (String.IsNullOrWhiteSpace(hostNameOrAddress))
{
pingStatusMessage = "Missing host name";
pingAddressMessage = "";
return false;
}
var data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
var buffer = Encoding.ASCII.GetBytes(data);
var timeout = 120;
using (var p = new Ping())
{
var options = new PingOptions();
options.DontFragment = true;
try
{
var r = p.Send(hostNameOrAddress, timeout, buffer, options);
if (r.Status == IPStatus.Success)
{
pingStatusMessage = "Ping to " + hostNameOrAddress.ToString() + "[" + r.Address.ToString() + "]" + " (Successful) "
+ "Bytes =" + r.Buffer.Length + " TTL=" + r.Options.Ttl + " Response delay = " + r.RoundtripTime.ToString() + " ms " + "\n";
pingAddressMessage = r.Address.ToString();
return true;
}
else
{
// just to know the ip for the website if they block the icmp protocol
pingStatusMessage = r.Status.ToString();
var ips = Dns.GetHostAddresses(hostNameOrAddress);
pingAddressMessage = String.Join(",", ips.Select(ip => ip.ToString()));
return false;
}
}
catch (PingException ex)
{
pingStatusMessage = string.Format("Error pinging {0}: {1}", hostNameOrAddress, (ex.InnerException ?? ex).Message);
pingAddressMessage = hostNameOrAddress;
return false;
}
}
}
Here I have extracted a utility method from the user interface code and also properly disposed of the Ping instance after it is no longer needed.
Then
TryPing(#"www.google.com", out pingStatusMessage, out pingAddressMessage);
Gives
Ping to www.google.com[146.115.8.83] (Successful) Bytes =32 TTL=62 Response delay = 8 ms
While
TryPing(#"www.kdjf98rglkfgjldkfjgdl;fge8org.com", out pingStatusMessage, out pingAddressMessage);
Gives
Error pinging www.kdjf98rglkfgjldkfjgdl;fge8org.com: No such host is known
I'm trying to write a voting server and client, so you start the program and it displays the voting form and you can vote on various items. For the server part I've got the server running in a separate thread, but it's using a lot of CPU, how do I reduce the amount of CPU it's using?
this is my server:
Form1 main = new Form1();
try
{
IPAddress ipAd = IPAddress.Parse(main.ipAddress); //use local m/c IP address, and use the same in the client
/* Initializes the Listener */
TcpListener myList = new TcpListener(ipAd, 55548);
/* Start Listeneting at the specified port */
myList.Start();
while (true)
{
string message = "";
Socket s = myList.AcceptSocket();
if (main.users.Contains(s.RemoteEndPoint.ToString()) == false)
main.users.Add(s.RemoteEndPoint.ToString());
byte[] b = new byte[500];
int k = s.Receive(b);
for (int i = 0; i < k; i++)
{
message += (Convert.ToString(b[i]));
}
string[] messageArray = message.Split('/');
MessageBox.Show("help");
if (messageArray[0].CompareTo("vote") == 0)
{
if (main.votes.ContainsKey(messageArray[1]) != true) main.votes.Add(messageArray[1], 1);
else main.votes[messageArray[1]]++;
string[] temp = main.textBox1.Text.Split(' ');
int numVotes = Convert.ToInt32(temp[1]);
numVotes++;
main.textBox1.Text = temp[0] + " " + Convert.ToString(numVotes);
}
if (messageArray[0].CompareTo("start") == 0)
{
main.updateEverything();
}
if(messageArray[0].CompareTo("withdraw") == 0)
{
main.votes[messageArray[1]]--;
string[] temp = main.textBox1.Text.Split(' ');
int numVotes = Convert.ToInt32(temp[1]);
numVotes--;
main.textBox1.Text = temp[0] + " " + Convert.ToString(numVotes);
}
/* clean up */
s.Close();
myList.Stop();
}
}
catch (Exception e)
{
Console.WriteLine("Error..... " + e.StackTrace);
}
You are using a blocking type of connection. The loop you create causes a CPU overhead because of the TcpListener.AcceptConnection(). Your solution is to accept non-blocking socket connections, which is done by receiving data from socket asynchronously.
Here's the msdn link that explains how it works.
http://msdn.microsoft.com/en-us/library/dxkwh6zw.aspx
I see you have string concatenations which basically affects performance; try using a StringBuilder - the message variable should be of type StringBuilder.
I have an app which pings IP or IP range. The problem is that when hosts are closed it takes longer to ping than they are open. When host is closed the time to ping is about 1-2 seconds.
How could I make it faster when hosts are closed?
This is my code:
using System;
using System.Text;
using System.Windows.Forms;
using System.Net.NetworkInformation;
namespace Range_Pinger
{
public partial class PingIPRange : Form
{
uint startIP, endIP, currentIP;
int count = 0;
int open = 0;
int closed = 0;
public PingIPRange()
{
InitializeComponent();
tmrPingInterval.Tick += new EventHandler(tmrPingInterval_Tick);
}
void tmrPingInterval_Tick(object sender, EventArgs e)
{
if (txtTo.Text == string.Empty) Ping(ip2str(startIP));
else
{
if (currentIP >= endIP) tmrPingInterval.Stop();
Ping(ip2str(currentIP));
currentIP++;
}
count++;
tsslPingCount.Text = "Total number of pings: " + count.ToString() +
" Open IPs: " + open.ToString() + " Closed IPs: " + closed.ToString();
}
static uint str2ip(string ip)
{
string[] numbers = ip.Split('.');
uint x1 = (uint)(Convert.ToByte(numbers[0]) << 24);
uint x2 = (uint)(Convert.ToByte(numbers[1]) << 16);
uint x3 = (uint)(Convert.ToByte(numbers[2]) << 8);
uint x4 = (uint)(Convert.ToByte(numbers[3]));
return x1 + x2 + x3 + x4;
}
static string ip2str(uint ip)
{
string s1 = ((ip & 0xff000000) >> 24).ToString() + ".";
string s2 = ((ip & 0x00ff0000) >> 16).ToString() + ".";
string s3 = ((ip & 0x0000ff00) >> 8).ToString() + ".";
string s4 = (ip & 0x000000ff).ToString();
return s1 + s2 + s3 + s4;
}
private void btnPing_Click(object sender, EventArgs e)
{
txtDisplay.Text = string.Empty;
tsslPingCount.Text = string.Empty;
count = 0;
open = 0;
closed = 0;
tmrPingInterval.Interval = int.Parse(nudInterval.Value.ToString());
try
{
startIP = str2ip(txtFrom.Text);
if (txtTo.Text != string.Empty) endIP = str2ip(txtTo.Text);
currentIP = startIP;
tmrPingInterval.Start();
}
catch
{
MessageBox.Show("Invalid input. It must be something like: 255.255.255.255");
}
}
private void btnStop_Click(object sender, EventArgs e)
{
tmrPingInterval.Stop();
}
private void Ping(string address)
{
Ping pingSender = new Ping();
PingOptions options = new PingOptions();
options.DontFragment = true;
string data = "01234567890123456789012345678901";
byte[] buffer = Encoding.ASCII.GetBytes(data);
int timeout = 120;
try
{
PingReply reply = pingSender.Send(address, timeout, buffer, options) ;
if (reply.Status == IPStatus.Success)
{
open++;
txtDisplay.AppendText("Host " + address + " is open." + Environment.NewLine);
}
else
{
closed++;
txtDisplay.AppendText("Host " + address + " is closed." + Environment.NewLine);
}
}
catch (Exception ex)
{
txtDisplay.SelectedText += Environment.NewLine + ex.Message;
}
}
private void tsmiExit_Click(object sender, EventArgs e)
{
this.Close();
}
}
}
This is what I have now:
[DllImport("iphlpapi.dll", ExactSpelling = true)]
public static extern int SendARP(IPAddress DestIP, int SrcIP, byte[] pMacAddr, ref uint PhyAddrLen);
private void Ping(IPAddress address)
{
byte[] macAddr = new byte[6];
uint macAddrLen = (uint)macAddr.Length;
if (SendARP(address, 0, macAddr, ref macAddrLen) == 0)
{
txtDisplay.AppendText("Host " + address + " is open." + Environment.NewLine);
}
else txtDisplay.AppendText("Host " + address + " is closed." + Environment.NewLine);
}
You shouldn't reduce the timeout. Try to send multiple pings at once async.
var ping = new Ping();
ping.PingCompleted += (sender, eventArgs) =>
{
// eventArgs.Reply.Address
// eventArgs.Reply.Status
};
ping.SendAsync(ip, etc.);
Your address is a string. Thus it will go via DNS first to see if this is possibly a hostname (even if it is an IP address).
I suggest you use the overload taking an IPAddress instead.
I created a live host scanner not too long ago. It uses ARP to check if a computer is online.
An ARP request is much faster than if you'd ping a host.
Here's the code I used to check if a Host is available:
//You'll need this pinvoke signature as it is not part of the .Net framework
[DllImport("iphlpapi.dll", ExactSpelling = true)]
public static extern int SendARP(int DestIP, int SrcIP,
byte[] pMacAddr, ref uint PhyAddrLen);
//These vars are needed, if the the request was a success
//the MAC address of the host is returned in macAddr
private byte[] macAddr = new byte[6];
private uint macAddrLen;
//Here you can put the IP that should be checked
private IPAddress Destination = IPAddress.Parse("127.0.0.1");
//Send Request and check if the host is there
if (SendARP((int)Destination.Address, 0, macAddr, ref macAddrLen) == 0)
{
//SUCCESS! Igor it's alive!
}
If you're interested Nmap also uses this technique to scan for available hosts.
ARP scan puts Nmap and its optimized algorithms in charge of ARP requests. And if it gets a response back, Nmap doesn't even need to worry about the IP-based ping packets since it already knows the host is up. This makes ARP scan much faster and more reliable than IP-based scans. So it is done by default when scanning ethernet hosts that Nmap detects are on a local ethernet network. Even if different ping types (such as -PE or -PS) are specified, Nmap uses ARP instead for any of the targets which are on the same LAN.
EDIT:
This only works within the current subnet! As long as there is no router between the requesting machine and the target it should work fine.
ARP is a non-routable protocol, and can therefore only be used between systems on the same Ethernet network. [...]
arp-scan can be used to discover IP hosts on the local network. It can discover all hosts, including those that block all IP traffic such as firewalls and systems with ingress filters. - Excerpt from NTA-Monitor wiki
For more information on the SendARP function you can check the pinvoke.net documentation.
You need to redesign your application to use multithreading -> tasks. Issue a task for each ping, and when you receive a response from a given host fire an event and update the UI. Changing socket timeout will only help you to reduce the timeout from outrageous to insufferable.
Not sure if this is any help (see final post on the thread), it seems an almost identical problem. What you're butting up against there is the protocol stack's timeout. You can get around it if you use socket to connect as you'll have more control.