Reconnecting to COM after lost connection - c#

I cannot reconnect to COM port after device on the other end abruptly drops connection.
I can connect again only if I close and re-open the application.
Here is my connection function:
public bool connectTurboPump()
{
try
{
if (TPumpSerialPort != null && TPumpSerialPort.IsOpen == true)
return true;
DataRow dr = tblTPump.Rows[0];
Types.Connection TPumpConnection = new Types.Connection();
TPumpConnection.PORT = dr["port"].ToString();
TPumpConnection.BAUD_RATE = Convert.ToInt32(dr["baud"]);
TPumpConnection.PARITY = (Parity)Enum.Parse(typeof(Parity), dr["parity"].ToString(), true);
TPumpConnection.DATA_BITS = Convert.ToInt32(dr["dataBits"]);
TPumpConnection.STOP_BITS = (StopBits)Enum.Parse(typeof(StopBits), dr["stopBits"].ToString(), true);
TPumpConnection.HANDSHAKE = (Handshake)Enum.Parse(typeof(Handshake), dr["handshake"].ToString(), true);
TPumpSerialPort = new SerialPort(TPumpConnection.PORT, TPumpConnection.BAUD_RATE, TPumpConnection.PARITY, TPumpConnection.DATA_BITS, TPumpConnection.STOP_BITS);
TPumpSerialPort.Handshake = TPumpConnection.HANDSHAKE;
TPumpSerialPort.Open();
TPumpSerialPort.NewLine = "\r";
TPumpSerialPort.ReadTimeout = 10000;
TPumpSerialPort.WriteTimeout = 10000;
if (TPumpSerialPort.IsOpen != true)
return false;
return true;
}
catch { return false; }
}
And here is my re-connection function:
public bool reconnectTurboPump(int attempts = 3)
{
try
{
if (TPumpSerialPort != null && TPumpSerialPort.IsOpen == true)
{
TPumpSerialPort.Close();
TPumpSerialPort.Dispose();
}
int i = 1;
while (true)
{
Log(string.Format("Reconnecting Turbo Pump attempt {0}", i));
if (connectTurboPump())
break;
if (i == attempts)
return false;
i++;
}
return true;
}
catch (Exception ex)
{
Log(string.Format("Could not reconnect to Turbo Pump: {0}", ex.Message));
return false;
}
}
Would really appreciate if someone could help.
Thank you.

This doesn't make much sense if this is a true serial port connection. There is no "connected" state, serial ports are very simple devices that have no underlying protocol that establishes a connection.
If this is actually a USB device that emulates a serial port then you'll indeed have this kind of problem. The driver that emulates the serial port invariably gets very sulky when you unplug the USB connector while the port is in use. There actually is a connection protocol for USB devices, the negotiation is done by the driver. They most typically make the port just disappear, this tends to give user code a heart-attack from which it can't recover. Behavior is very unpredictable and varies from one driver to another. There is no cure for this, glue the connector to the port and never assume that unplugging it will solve any problems in your code, even though that's the only thing you can do with USB.

Following Thomas' advice I've changed reconnection script to the following. Now in testing.
public bool reconnectTurboPump(int attempts = 3)
{
try
{
//if (TPumpSerialPort != null && TPumpSerialPort.IsOpen == true)
if (TPumpSerialPort != null)
{
TPumpSerialPort.Close();
TPumpSerialPort.Dispose();
}
int i = 1;
while (true)
{
Log(string.Format("Reconnecting Turbo Pump attempt {0}", i));
Thread.Sleep(2000);
if (connectTurboPump())
break;
if (i == attempts)
return false;
i++;
}
return true;
}
catch (Exception ex)
{
Log(string.Format("Could not reconnect to Turbo Pump: {0}", ex.Message));
return false;
}
}

Related

C# Modbus: ReadHoldingRegisters not consistent in returning ouput

I'm trying to search for a modbus that is connected to a serial port on my device and run some commands against it. However it seems to be that it is rather inconsistent at reading the holding registers. I'm using the NModbus4 library.
I have a thread running every second that checks for available serial ports and tries to get a connection:
// === Check if port is open, start connecting procedure if closed
if (!(serialPort.IsOpen))
{
List<string> portnames = new List<string>();
foreach (string s in SerialPort.GetPortNames())
{
portnames.Add(s); // COM3
}
// === Loop through the listed ports and try to connect
foreach (string portname in portnames)
{
if (!(serialPort.IsOpen))
{
try
{
serialPort.PortName = portname;
serialPort.BaudRate = 57600;
serialPort.DataBits = 8;
serialPort.Parity = Parity.None;
serialPort.StopBits = StopBits.Two;
serialPort.Open();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}
// === Check again after connecting procedure if the port is open
if (serialPort.IsOpen)
{
// === Check number of open ports
if (SerialPort.GetPortNames().Count() != 0)
{
// === Check if master is defined
if (master == null)
{
// === Define master
master = ModbusSerialMaster.CreateRtu(serialPort);
}
// === If master is already defined
if (master != null)
{
int parameterID = this.readParameterSetID();
if (parameterID != 0)
{
if (!desk_devices.Contains(parameterID))
{
desk_devices.Add(parameterID);
newDevice = true;
}
}
}
} else
{
// === No serial ports available, close the opened serial port
serialPort.Close();
}
}
The readParameterSetID function is as following:
private int readParameterSetID()
{
int parameterSetID = 0;
if (mutex)
{
Console.WriteLine("---Poll overrun----");
}
mutex = true;
if (serialPort.IsOpen)
{
if (master != null)
{
try
{
ushort[] holding_register = master.ReadHoldingRegisters(1, 107, 1); // Fails here
for (int i = 0; i < 1; i++)
{
int tmpStart = Convert.ToInt32(107 + i);
String tempStart = Convert.ToString(tmpStart, 16);
int tmpReg = Convert.ToInt32(holding_register[i]);
String tempReg = Convert.ToString(tmpReg, 16);
parameterSetID = holding_register[i];
}
} catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
mutex = false;
return parameterSetID;
}
Now the weird thing happening here is that the readParameterSetID function should return a 7 or a 0 when executed correctly. However sometimes it just stops after reading the holding registers. mutex will stay true and nothing gets returned. When I reboot the application it either works or the same thing happens again.
I don't know why it's working in 50% of the situations and the other 50% returns nothing. I've tried to redefine the master if the function readParameterSetID fails, however I noticed that once every couple second the master will be reset resulting in commands further down the line not being send.
I'd love to know why this is happening or a way to fix this and read the holding registers without the communication failing.

Sniffing TCP packets of an android device on local network

I am writing a packet sniffer using C# and PcapDotNet, I've successfully implemented the feature and I was able to capture all the TCP packets from my laptop, the problem is if I target my android device I get no packets at all,
I am a beginner at networking, so I think I am probably missing something.
This is my code for the packet handler, I am using an ObjectListView with a model object that contains all the information I want to list, also I have the network adapter in Promiscuous mode with a Berkeley filter to get only TCP packets with port numbers 443 and 80 that contains data (No SYN, FIN, ACK-only packets) from a specific Mac address
Code:
private void PacketHandler(PcapDotNet.Packets.Packet packet)
{
if (packet == null) { return; }
if (packet.Ethernet == null) { return; }
if (packet.Ethernet.IpV4 == null) { return; }
if (packet.Ethernet.IpV4.Tcp == null) { return; }
if (packet.Ethernet.IpV4.Tcp.Http == null) { return; }
var acpacket = new AcceptedPacket(); //Model Object
acpacket.Packet = packet;
try
{
HttpDatagram http = packet.Ethernet.IpV4.Tcp.Http;
if (packet.Ethernet.Source.ToString() == targetmac)
{
if (http.IsRequest && http.IsValid)
{
if (materialListView1.InvokeRequired)
{
materialListView1.BeginInvoke(new Action(() => {
materialListView1.AddObject(acpacket); }));
}
else
{
materialListView1.AddObject(acpacket);
}
ListofAcceptedPackets.Add(acpacket);
}
}
}
catch (Exception ex)
{
MetroMessageBox.Show(this, ex.Message, "Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
And this how I opened the adapter :
using (communicator =
selectedDevice.Open(65536,PacketDeviceOpenAttributes.Promiscuous,
1000))
{
if (communicator.DataLink.Kind !=
DataLinkKind.Ethernet)
{
if (MetroMessageBox.Show(this, "Only Ethernet is supported in this operation!","Error", MessageBoxButtons.OK,MessageBoxIcon.Error) == DialogResult.OK)
{
return;
}
}
using (BerkeleyPacketFilter filter = communicator.CreateFilter($"tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0) and ether src {targetmac.ToLower()} or tcp port 443 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0) and ether src {targetmac.ToLower()}"))
{
communicator.SetFilter(filter);
}
flag = true;
Packet packet;
do
{
PacketCommunicatorReceiveResult result =
communicator.ReceivePacket(out packet);
switch (result)
{
case
PacketCommunicatorReceiveResult.Timeout:
continue;
case
PacketCommunicatorReceiveResult.Ok:
{
PacketHandler(packet);
}
break;
default:
break;
}
} while (flag);
}
I also tried without any filter and couldn't reach the android device packets.
so is it possible to capture TCP packets from other devices on the network besides my own device? or maybe there's something wrong with my implementation?
Edit: after trying a bit more I was able to successfully get the TCP packets from the android device but only while implementing ARP Cache Poisoning at the same time because the device thinks that I am the gateway. so my question stands, can this be done without ARP Cache Poisoning because I think it's a bit aggressive for a packet sniffer.
I was finally able to make it work by applying ARP cache poisoning while the code below is able to redirect packets for any device to their destination, this way you get to capture packets for any device on the network specifically without losing internet access for this device.
Code:
private void StartSniffer()
{
RawCapture rawCapture;
do
{
if ((rawCapture = capturedevice.GetNextPacket()) != null)
{
EthernetPacket Packet = PacketDotNet.Packet.ParsePacket(rawCapture.LinkLayerType, rawCapture.Data) as EthernetPacket;
if (Packet == null) { return; }
AcceptedPacket acPacket = new AcceptedPacket();
acPacket.Packet = Packet;
if (Packet.SourceHwAddress.Equals(TargetMAC))
{
Packet.SourceHwAddress = capturedevice.MacAddress;
Packet.DestinationHwAddress = GatewayMAC;
capturedevice.SendPacket(Packet);
if (acPacket.TCPPacket != null &&
((acPacket.Type.Equals("HTTPS") && acPacket.TCPPacket.PayloadData != null) ||
(acPacket.Type.Equals("HTTP") && acPacket.TCPPacket.PayloadData != null)))
{
materialListView1.BeginInvoke(new Action(() =>
{
materialListView1.AddObject(acPacket);
if (materialListView1.Items.Count > 15 && !ResizeDone)
{
olvColumn8.MaximumWidth = 65;
olvColumn8.MinimumWidth = 65;
olvColumn8.Width = 65;
ResizeDone = true;
}
ListofAcceptedPackets.Add(acPacket);
}));
}
}
else if (Packet.SourceHwAddress.Equals(GatewayMAC))
{
IPv4Packet IPV4 = Packet.Extract(typeof(IPv4Packet)) as IPv4Packet;
if (IPV4.DestinationAddress.Equals(Target))
{
Packet.SourceHwAddress = capturedevice.MacAddress;
Packet.DestinationHwAddress = TargetMAC;
capturedevice.SendPacket(Packet);
}
if (Properties.Settings.Default.PacketDirection == "Inbound")
{
if (acPacket.TCPPacket != null &&
((acPacket.Type.Equals("HTTPS") && acPacket.TCPPacket.PayloadData != null) ||
(acPacket.Type.Equals("HTTP") && acPacket.TCPPacket.PayloadData != null)))
{
materialListView1.BeginInvoke(new Action(() =>
{
materialListView1.AddObject(acPacket);
if (materialListView1.Items.Count > 15 && !ResizeDone)
{
olvColumn8.MaximumWidth = 65;
olvColumn8.MinimumWidth = 65;
olvColumn8.Width = 65;
ResizeDone = true;
}
ListofAcceptedPackets.Add(acPacket);
}));
}
}
}
}
} while (snifferStarted);
And this is the capture device setup:
try
{
snifferStarted = true;
if (capturedevice != null)
{
capturedevice.Open(DeviceMode.Promiscuous, 1000);
capturedevice.Filter = $"(ip and ether src {targetmac.ToLower()}) or (ip and ether src {gatewayMAC.ToLower()} and dst net {Target})";
new Thread(() => { StartSniffer(); }).Start();
}
else
{
MetroMessageBox.Show(this, "No Capture Device is selected!", "Error", MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
catch (Exception exception)
{
MetroMessageBox.Show(this, exception.Message, "Error", MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
Note: this has been done using Packet.Net NOT PcapDotNet.

Exception after reset serial connection to a different Baud rate

In my application I'm using a 9600 baud rate serial connection and I want to use a 115200 baud rate connection for data transfer.
I've disconnected from the old connection and set it to be null value, and set my serial connection to new connection with different baud rate.
The connection is unstable and I sometimes get a System.ObjectDisposedException - what did I miss?
The connection code
public string startConnection()
{
if (serial != null)
{
serial.Dispose();
}
foreach (string portname in SerialPort.GetPortNames())
{
serial = new SerialPort(portname, 9600, Parity.None, 8, StopBits.One);
serial.ReadTimeout = 5000;
serial.WriteTimeout = 5000;
serial.Handshake = System.IO.Ports.Handshake.None;
serial.NewLine = "\n";
string received = "";
try
{
serial.Open();
serial.DiscardInBuffer();
serial.Write(":09;BATTERY;");
Thread.Sleep(500);
received = serial.ReadLine();
if (received.Contains(";BATTERY;V="))
{
status = SERIAL_CONNECTED;
return portname;
}
}
catch (Exception err)
{
try
{
serial.Close();
status = DISCONNECTED;
}
catch (Exception)
{
// throw;
}
}
}
throw new Exception("couldn't connect to coms");
//return "couldn't connect to coms";
//this.Close();
}
Disconnect function:
public void disconnect ()
{
if (serial == null || serial.IsOpen==false ||status == DISCONNECTED)
return;
status = DISCONNECTED;
serial.Close();
serial = null;
}
The main program is:
private async void BurnOFP_click(object sender, RoutedEventArgs e)
{
startConnection();
some actions.............
disconnect();
var t = new Task(() =>
{
try
{
myUswm.startModemConnection(); // same but with different baud rate
}
catch (Exception e2) { MessageBox.Show(e2.Message); }
});
t.Start();
t.Wait();
modem = new XMODEM_FullDotNET(myUswm.getSerialPort(), XMODEM_FullDotNET.Variants.XModemCRC);
buff = File.ReadAllBytes(softwareFilePath_Text.Text);
if (buff.Length < 1)
{
MessageBox.Show("ERROR : wrong OFP file");
return;
}
if (myUswm.prepareOFPBurning()) // sends u to start transfer
{
if (isBurning == false)
{
isBurning = true;
modem._ProgressSent = 0;
myProgBar = new myProgressBar(modem);
myProgBar.StartTransfer(modem, buff.Length);
myProgBar.Show(); // show window
// got the Exception here!!!!!!!!!!
var t3 = new Task(() =>
{
modem.Send(buff);
});
............
}
else
MessageBox.Show("burning in progress..");
}
}
catch (Exception e1)
{
MessageBox.Show(e1.Message);
}
}
Thanks for any help
RESOLVED
my problem was A bad timing caused by closing and reopen the same port.
I've found the information in MSDN Serial class:
The best practice for any application is to wait for some amount of time after calling the Close method before attempting to call the Open method, as the port may not be closed instantly.
my solution was keeping the connection alive and change the baud rate and update the connection status in my application manually.

Loop get paused/diactivated inside catch() block

I am trying to send AT commands to COM ports, so I can find the GSM dongle. Below is the code
public bool findGsmModem()
{
bool sendStatus = false;
//Get all the available ports
string[] serialPorts = SerialPort.GetPortNames();
for (int i = 0; i < serialPorts.Length; i++)
{
Console.WriteLine(serialPorts[i]);
}
//Iterate through all the ports sending AT commands to find a modem
for (int i = 0; i < 1; i++)
{
try
{
//port.PortName = serialPorts[i].Trim();
port.PortName = "COM7";
openPort();
string res = ATCommandCaller("AT", 300,"Unable to connect to the phone"); //Connecting to the phone
//res = ATCommandCaller("AT+CMGF=1", 300); //Setting the message Format
sendStatus = true;
break;
}
catch (System.InvalidOperationException ex)
{
//port.PortName = null;
port.Close();
autoInitializer();
//port = new SerialPort();
continue;
//throw ex;
}
}
return sendStatus;
}
Here is how I call this method inside another class
if (sms.findGsmModem())
{
MessageBox.Show("Modem Found: " + sms.getPortName());
}
else
{
MessageBox.Show("No modem found");
}
OK, now in the findGsmModem() method if I use port.PortName = "COM5"; the above second code works successfully and display the message. That is because the Modem is actually in COM5 and the value is hard coded, so the statement do not reach the catch() block.
But, if I use port.PortName = serialPorts[i].Trim(); or port.PortName = serialPorts[i]; then it seems like nothing is happening instead of printing the port names (inside findGsmModem()). Following ports are being printed
COM1
COM2
COM8
COM9
COM5
COM4
COM3
As you can see, the COM5, the port where the gms modem actually exists is in the 5th element of the array, so findGsmModem() calls catch() part before it access the COM5.
I do believe I am not getting anything when port.PortName = serialPorts[i].Trim() is used because it goes to the catch() part and something terrible happens there.
Any idea?
Here is the openPort() method
public void openPort()
{
try
{
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
if (!port.IsOpen)
{
port.Open();
}
port.RtsEnable = true;
port.DtrEnable = true;
}
catch (Exception ex)
{
throw ex;
}
}
EDIT
Here is the most weirdest part. I just noticed the catch() block never get reached when the loop is called! I tried ex.Message to print the stack trace, and it didn't print anything!
catch (Exception ex)
This is the trouble with catch-em-all exception handling. You are getting an InvalidOperationException because you change the PortName property on a opened port. That's a bug in your code, nothing actually went wrong with the serial port.
You'll need to call the Close() method if you find out that it port is not connected to the GSM modem.
Then you can't call Open() again on that same SerialPort instance, it takes time for internal worker thread to shut down. Best thing to do is to create a new instance of SerialPort instead of trying to keep using the same one repeatedly.

Find gsm modem port in c#

I want to loop through the available ports:
System.IO.Ports.SerialPort.GetPortNames()
to find if a port is used by a gsm modem.
Any idea please.
What I did in my application for one similar task:
To check that a modem is connected to particular port you can send AT command into this port.
This function below returns true if we found a modem on the current COM port:
private bool CheckExistingModemOnComPort(SerialPort serialPort)
{
if ((serialPort == null) || !serialPort.IsOpen)
return false;
// Commands for modem checking
string[] modemCommands = new string[] { "AT", // Check connected modem. After 'AT' command some modems autobaud their speed.
"ATQ0" }; // Switch on confirmations
serialPort.DtrEnable = true; // Set Data Terminal Ready (DTR) signal
serialPort.RtsEnable = true; // Set Request to Send (RTS) signal
string answer = "";
bool retOk = false;
for (int rtsInd = 0; rtsInd < 2; rtsInd++)
{
foreach (string command in modemCommands)
{
serialPort.Write(command + serialPort.NewLine);
retOk = false;
answer = "";
int timeout = (command == "AT") ? 10 : 20;
// Waiting for response 1-2 sec
for (int i = 0; i < timeout; i++)
{
Thread.Sleep(100);
answer += serialPort.ReadExisting();
if (answer.IndexOf("OK") >= 0)
{
retOk = true;
break;
}
}
}
// If got responses, we found a modem
if (retOk)
return true;
// Trying to execute the commands without RTS
serialPort.RtsEnable = false;
}
return false;
}
On the next stage we can collect some data from the modem.
I used the following commands:
ATQ0 - switch on confirmations (receive OK on each request)
ATE0 - switch on echo
ATI - get modem details
ATI3 - get extended modem details (not all modems supports this command)
// Check each Availble COM port
foreach (string l_sport in l_available_ports)
{
GlobalVars.g_serialport = GlobalFunc.OpenPort(l_sport, Convert.ToInt32(this.cboBaudRate.Text), Convert.ToInt32(this.cboDataBits.Text), Convert.ToInt32(this.txtReadTimeOut.Text), Convert.ToInt32(this.txtWriteTimeOut.Text));
if (GlobalVars.g_serialport.IsOpen)
{
GlobalVars.g_serialport.WriteLine("AT\r");
Thread.Sleep(500);
string l_response = GlobalVars.g_serialport.ReadExisting();
if (l_response.IndexOf("OK") >= 0)
{
GlobalVars.g_serialport.WriteLine("AT+CMGF=1\r");
Thread.Sleep(500);
string l_response1 = GlobalVars.g_serialport.ReadExisting();
if (l_response1.IndexOf("OK") >= 0)
{
GlobalVars.g_PhoneNo = txt_PhNum.Text;
MessageBox.Show("Connected Successfully", "Connection", MessageBoxButtons.OK, MessageBoxIcon.Information);
lblConnectionStatus.Text = "Phone Connected Successfully.";
btnOK.Enabled = false;
btnDisconnect.Enabled = true;
GlobalVars.g_serialport.WriteLine("AT+CGSN\r");
Thread.Sleep(500);
string l_imei = GlobalVars.g_serialport.ReadExisting();
Console.WriteLine("Modem IMEI:" + l_imei);
if (l_imei.IndexOf("OK", 1) > 0)
{
l_imei = l_imei.Replace("AT+CGSN\r\r\n", null);
l_imei = l_imei.Replace("\r\n\r\nOK\r\n", null);
lbl_ModemIMEI.Text = l_imei;
}
else
{
lblConnectionStatus.Text = "Phone Connected Successfully. Error reading IMEI.";
}
EnableSMSNotification(GlobalVars.g_serialport);
break;
}
else
{
Console.WriteLine("No AT+CMGF cmd response");
}
}
else
{
Console.WriteLine("No AT cmd response");
}
}
else
{
Console.WriteLine("No Phone At:" + l_sport);
}
}

Categories