I dont know if this is a good way to work with a stack for this task but I'm sure there is a faster way ...
I get data from my microcontroller but the data length is not always the same length.
I thought maybe I can push data in my stack and in a thread I can pop it and decode the message. I didnt wanted slow down the DataReceivedHandler so then I created a Thread which can pop the data and write it to my Listview in my decodeMessage() function.
After a short time I get a System.OutOfMemories Exception..
Any ideas how I can do it in a better way ?
I'm reading from my serial port just when data arrives here:
Stack<byte[]> stack = new Stack<byte[]>();
.....
public void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
byte[] data = new byte[sp.BytesToRead];
sp.Read(data, 0, data.Length);
stack.Push(data);
}
And this is my Thread:
private void formatData()
{
try
{
while (true)
{
byte[] data;
int i=0;
Dispatcher.BeginInvoke(new Action(() =>
{
while (stack.Count > 0)
{
data = stack.Pop();
while (i < data.Length)
{
decodeMessage(data[i]);
i++;
}
}
}));
}
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
thx
This code use a thread safe queue. I simplified some of my own code, so this code is not tested or compiled. If you have problems compiling or it produce errors, add a comment to me and I will help you out.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.Windows.Threading;
using System.Collections.Concurrent;
void someRoutine()
{
// initialize queue before using it
serialDataQueue = new ConcurrentQueue<char>();
}
/// <summary>
/// data from serialPort is added to the queue as individual chars,
/// a struct may be better
/// </summary>
public ConcurrentQueue<char> serialDataQueue;
// get data
void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = sender as SerialPort;
int bytesAvailable = sp.BytesToRead;
// array to store the available data
char[] recBuf = new char[bytesAvailable];
try
{
// get the data
sp.Read(recBuf, 0, bytesAvailable);
// put data, char by char into a threadsafe FIFO queue
// a better aproach maybe is putting the data in a struct and enque the struct
for (int index = 0; index < bytesAvailable; index++)
serialDataQueue.Enqueue(recBuf[index]);
}
catch (TimeoutException ex)
{
// handle exeption here
}
}
/// <summary>
/// Check queue that contains serial data, call this
/// routine at intervals using a timer or button click
/// or raise an event when data is received
/// </summary>
private void readSearialDataQueue()
{
char ch;
try
{
while (serialDataQueue.TryDequeue(out ch))
{
// do something with ch, add it to a textbox
// for example to see that it actually works
textboxDataReceived.Text += ch;
}
}
catch (Exception ex)
{
// handle ex here
}
}
Related
My code working perfectly if the SMS messages to be sent has less than 70 characters.
I want to send messages with between 70 to 200 characters per message.
using GsmComm.GsmCommunication;
using GsmComm.PduConverter;
using GsmComm.Server;
using GsmComm.PduConverter.SmartMessaging;
namespace SMSSender
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
try
{
string msg = " کو ہم نے";
GsmCommMain comm = new GsmCommMain(4, 19200, 500);
comm.Open();
SmsSubmitPdu pdu;
pdu = new SmsSubmitPdu(msg, "03319310077", DataCodingScheme.NoClass_16Bit);
comm.SendMessage(pdu);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
If you take a look at the source here for the SmsPdu class here, you can see that there's an explicit limit of 70 Unicode characters, which would explain the issue you are encountering:
public abstract class SmsPdu : ITimestamp
{
// Omitted for brevity
/// <summary>
/// Gets the maximum Unicode message text length in characters.
/// </summary>
public const int MaxUnicodeTextLength = 70;
// Omitted for brevity
}
Possible Workaround
A possible workaround might involve breaking a single message into multiple batches of less than 70 characters and sending multiple to the same destination:
public static IEnumerable<string> BatchMessage(string message, int batchSize = 70)
{
if (string.IsNullOrEmpty(message))
{
// Message is null or empty, handle accordingly
}
if (batchSize < message.Length)
{
// Batch is smaller than message, handle accordingly
}
for (var i = 0; i < message.Length; i += batchSize)
{
yield return message.Substring(i, Math.Min(batchSize, message.Length - i));
}
}
And then simply call that prior to sending your message and send the batches individually:
// Open your connection
GsmCommMain comm = new GsmCommMain(4, 19200, 500);
comm.Open();
// Store your destination
var destination = "03319310077";
// Batch your message into one or more
var messages = BatchMessage(" کو ہم نے");
foreach (var message in messages)
{
// Send each one
var sms = new SmsSubmitPdu(message, destination, DataCodingScheme.NoClass_16Bit);
comm.SendMessage(sms);
}
Please see my code below, that I am trying to return back to method that I registered the port's DataReceived event. Basically, If I recieve data from port before read time out. I will return back where I registered DataReceived event and degister and continue process. I am trying to do it with while loop. But not sure if it is accurate, and it is the way that has to be done
or if there is any other way to do this.
public class CommClass{
private static byte[] portReturn = null;
private void setUpDevice()
{
byte[] command = { 0x11,0X51 };
try
{
port.DataReceived += new SerialDataReceivedEventHandler(serialPortDataReceived);
port.Write(command, 0, command.Length);
while (portReturn == null) { } //Not sure if this will work. If I receive data before times out I do not want to wait in the loop.
port.DataReceived -= serialPortDataReceived;
}
catch(Exception ex)
{
//to do
}
}
private void serialPortDataReceived(object sender, SerialDataReceivedEventArgs e)
{
var servicePort = (SerialPort)sender;
portReturn = servicePort.ReadByte();
return;
}
}
You code will technically work; however, your while loop will max out your CPU while you're waiting for data to come in, which is not what you want. I recommend using a ManualResetEvent here to let you wait to receive data in a CPU friendly way. You can read more about them here
public class CommClass
{
private static byte[] portReturn = null;
// ManualResetEvents are great for signaling events across threads
private static ManualResetEvent dataReceivedEvent = new ManualResetEvent(false);
private void setUpDevice()
{
byte[] command = { 0x11,0X51 };
try
{
port.DataReceived += new SerialDataReceivedEventHandler(serialPortDataReceived);
port.Write(command, 0, command.Length);
// Wait for the event to be set without spinning in a loop.
// Can also specify a timeout period to wait in case the data never comes.
dataReceivedEvent.WaitOne();
// Reset the event so that you can use it again later if necessary
dataReceivedEvent.Reset();
port.DataReceived -= serialPortDataReceived;
}
catch(Exception ex)
{
//to do
}
}
private void serialPortDataReceived(object sender, SerialDataReceivedEventArgs e)
{
var servicePort = (SerialPort)sender;
portReturn = servicePort.ReadByte();
// Set the event to let the main thread know you have received data
dataReceivedEvent.Set();
}
}
I am having the following issue:
Once I close my WM6 application and then try to start it again i get this error:
Only one usage of each socket address (protocol/network address/port) is normally permitted at System.Net.Sockets.Socket.Bind(EndPoint localEP)
at
System.Net.Sockets.Socket.TcpListener.Start()
...
I think this is due to the time interval for the connection to timeout, so I would like to close all open conections and force it to create a new connection, is this the correct way to proceed or is there a different way to handle this?
Here is the code used to start listening:
/// <summary>
/// Listens Asynchronously to Clients, creates a recieveMessageHandler to process the read.
///
/// Check WIKI, TODOS
/// </summary>
/// <returns></returns>
public void Listen()
{
myTcpListener.Start();
while (true)
{
//blocks until a client has connected to the server
try
{
TcpClient myTcpClient = myTcpListener.AcceptTcpClient();
DateTime now = DateTime.Now;
//Test if it's necessary to create a client
ClientConnection client = new ClientConnection(myTcpClient, new byte[myTcpClient.ReceiveBufferSize]);
// Capture the specific client and pass it to the receive handler
client.NetworkStream.BeginRead(client.Data, 0, myTcpClient.ReceiveBufferSize, r => receiveMessageHandler(r, client), null);
}
catch (Exception excp)
{
Debug.WriteLine(excp.ToString());
}
}
}
Yes, your server socket is likely in the TIME_WAIT state.
You can access the underlying ServerSocket and then use SetSocketOption and specify ReuseAddress.
I'm going to guess here that ClientConnection is your DLL, because I don't see that already included in the CF.
You don't really need that, though, if you declare MethodInvoker.
public delegate void MethodInvoker(); // required
To make your code really slick, you should also create your very own EventArgs class:
public class WmTcpEventArgs : EventArgs {
private string data;
public WmTcpEventArgs(string text) {
data = text;
}
public string Data { get { return data; } }
}
Very simple. With this new WmTcpEventArgs class and, you should be all set to receive your data that could post to something like a TextBox control:
private void NetworkResponder(object sender, WmTcpEventArgs e) {
textBox1.Text = e.Data;
}
Instead of coding a while(true) in your code, I prefer to include a little Boolean variable
private bool abortListener;
The code would look something like this:
public void Listen() {
listener.Start();
while (!abortListener) {
try {
using (var client = listener.AcceptTcpClient()) {
int MAX = client.ReceiveBufferSize;
var now = DateTime.Now;
using (var stream = client.GetStream()) {
Byte[] buffer = new Byte[MAX];
int len = stream.Read(buffer, 0, MAX);
if (0 < len) {
string data = Encoding.UTF8.GetString(buffer, 0, len);
MethodInvoker method = delegate { NetworkResponder(this, new WmTcpEventArgs(data)); };
abortListener = ((form1 == null) || form1.IsDisposed);
if (!abortListener) {
form1.Invoke(method);
}
}
}
}
} catch (Exception err) {
Debug.WriteLine(err.Message);
} finally {
listener.Stop();
}
}
}
Notice you are still catching your Exceptions, but you also stop the TcpListener.
I need event, that will call my function after full line received, not just one byte.
SerialPort object in .NET has 3 events: DataReceived, ErrorReceived, PinChanged.
When im using DataReceived - event is "firing" after 1 byte, or after "x" bytes defined in "ReceiveByteThreshold" property. Line length may vary, so i cant predict "x".
Can someone give me a hint?
I have to create some buffer, which will collect bytes until LF/CRLF, or there is better approach to problem?
You cannot get this, the only option is SerialPort.ReceivedBytesThreshold to delay the DataReceived event handler call and that's useless for a variable length response.
The workaround is very simple, just call ReadLine() in your DataReceived event handler. That will block on a worker thread, not affecting anything else going on in your program. No danger either of additional events firing while the ReadLine() call is blocking, it is interlocked inside the SerialPort class. Use the ReadTimeout property if necessary if the communication isn't reliable enough so ReadLine() will not block forever. Set it to ten times the expected delay in receiving the longest possible response.
You'll have to do it yourself. Use DataReceived and check each byte. Collect the bytes in a buffer until you get a newline and then handle the buffer as a line at that point.
The hint:
The SerialPort class has a property NewLine to set the value used to interpret the end of a call to the ReadLine method.
Here is my quickly implemented, non blocking, same thread solution. It is a very basic state machine that waits for '\r' and '\n' and then sends all the buffered characters for parsing. You can alter it to whatever line-break value you want by changing the state machine itself.
In this approach you can register for the OnNewLineReceived event and process the data from the SerialStringMessgae eventhandler.
No try/catch overhead. No deadlocks.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace NonBlockingSerialPortReadLine
{
public partial class Form1 : Form
{
System.IO.Ports.SerialPort sp = new System.IO.Ports.SerialPort();
public event EventHandler OnNewLineReceived;
System.Windows.Forms.Timer NewDataTimer = new System.Windows.Forms.Timer();
int StateMachine = 0;
StringBuilder stringBuffer = new StringBuilder();
public Form1()
{
InitializeComponent();
InitTimer();
InitOnNewLineReceived();
}
private void InitTimer()
{
NewDataTimer.Interval = 50;
NewDataTimer.Tick += NewDataTimer_Tick;
}
private void InitOnNewLineReceived()
{
OnNewLineReceived += Form1_OnNewLineReceived;
}
void Form1_OnNewLineReceived(object sender, EventArgs e)
{
SerialStringMessgae STM = e as SerialStringMessgae;
string messgae = STM.message;
// PARSE YOU MESSAGE HERE - the debug line below is not mandatory
System.Diagnostics.Debug.WriteLine(messgae);
}
class SerialStringMessgae : EventArgs
{
public string message;
}
private void StartListeningButton_Click(object sender, EventArgs e)
{
StartListeningButton.Enabled = false;
sp = new System.IO.Ports.SerialPort("COM4",57600, System.IO.Ports.Parity.None, 8, System.IO.Ports.StopBits.One);
try
{
sp.Open();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
return;
}
if (sp.IsOpen)
{
NewDataTimer.Enabled = true;
}
}
void NewDataTimer_Tick(object sender, EventArgs e)
{
string newData = sp.ReadExisting();
foreach (char c in newData)
{
switch (StateMachine)
{
case 0:
// waiting for '\r'
if (c == '\r')
{
StateMachine = 1;
}
else
{
stringBuffer.Append(c);
}
break;
case 1:
// waiting for '\n'
if (c == '\n')
{
if (OnNewLineReceived != null)
{
SerialStringMessgae STM = new SerialStringMessgae();
STM.message = stringBuffer.ToString();
OnNewLineReceived(this, STM);
}
}
// after parsing the message we reset the state machine
stringBuffer = new StringBuilder();
StateMachine = 0;
break;
}
}
}
}
}
Hey all. I'm writing a simple client/server application (just for the experience, networking is fairly new to me) where the client sends the server data and the server outputs it to a textbox. It all works fine, except for one small detail... It seems sometimes a connection is made, but the data isn't being sent or read (can't work out which) and thus nothing is being outputted in the textbox. Every time a connection is made a counter is incremented, same thing when a data block is received. When you compare the two, the number of connections is correct but the data counter is usually lower, sometimes by as much as half. Anyway, if anyone can give me some advice or point me in the right direction, it would be greatly appreciated!
Here's the code if you require it:
(SERVER_CODE)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace Server
{
public partial class Form1 : Form
{
public int Connections = 0;
public int blocks = 0;
public int threads = 0;
public Thread MasterThread;
public TcpListener Master;
public volatile bool Run;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
public void StartMaster()
{
Master = new TcpListener(IPAddress.Any, 1986);
Master.Start();
MasterThread = new Thread(new ThreadStart(RunMaster));
MasterThread.Start();
}
public void RunMaster()
{
threads++;
label6.Text = String.Format("{0}", threads);
while (Run)
{
TcpClient client = Master.AcceptTcpClient();
Connections++;
label4.Text = String.Format("{0}", Connections);
Thread ClientThread = new Thread(new ParameterizedThreadStart(RunClient));
ClientThread.Start(client);
}
Master.Stop();
threads--;
label6.Text = String.Format("{0}", threads);
}
public void RunClient(object tcpClient)
{
TcpClient client = (TcpClient)tcpClient;
byte[] buffer = new byte[4096];
int byteCount = 0;
NetworkStream stream = client.GetStream();
threads++;
label6.Text = String.Format("{0}", threads);
while (Run)
{
try
{
byteCount = stream.Read(buffer, 0, 4096);
}
catch
{
//Connections--;
break;
}
if (byteCount == 0)
{
//Connections--;
break;
}
blocks++;
label5.Text = String.Format("{0}", blocks);
textBox1.AppendText(Encoding.ASCII.GetString(buffer, 0, byteCount) + "\r\n");
}
client.Close();
threads--;
label6.Text = String.Format("{0}", threads);
}
private void button1_Click(object sender, EventArgs e)
{
Run = true;
StartMaster();
}
private void button2_Click(object sender, EventArgs e)
{
Run = false;
}
}
}
(CLIENT_CODE)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace Client
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1986);
TcpClient client = new TcpClient();
try
{
client.Connect(endPoint);
}
catch
{
MessageBox.Show("Connect Error");
}
NetworkStream stream = client.GetStream();
byte[] data = Encoding.ASCII.GetBytes(textBox1.Text);
stream.Write(data, 0, data.Length);
stream.Flush();
client.Close();
}
}
}
Thank-you,
Tristan!.
Well, to start with you're crippling your own diagnostics with this:
catch
{
//Connections--;
break;
}
Why are you swallowing exceptions without any logging etc? Maybe an exception is being thrown, and you have no way of knowing. Ideally you should catch specific exceptions, and when you do catch an exception at least log what's going on.
At the other end of the spectrum, Wireshark should help you to work out whether the data is being sent or not.
I haven't had a thorough look at your code yet, but after a quick glance, you access variables from multiple threads without proper locking. A statement like x++; has to read the value of x, increment it, and write it back. Now if you have two threads doing this, you might run into this situation:
x = 0
Thread 1 Thread 2
------------------------
Read (0)
Read (0)
Increment (1)
Increment (1)
Write (1)
Write (1)
=> x = 1 instead of 2
If you need to access variables from multiple threads, ALWAYS synchronize unless you know exactly what you're doing. For example, create and use a synchronization object like this:
int threads = 0;
object threadSync = new object();
...
lock (threadSync) {
threads++;
}
Then only one thread may access the variable at a time and values are incremented correctly.
Edit: Another problem is that you access visible controls from a different thread than the one that created them. Early .NET versions allowed this, but the newer don't. If you need to update status messages, you need to look at the control's InvokeRequired property and if set to true, use Control.Invoke(...) to call a method that sets the property.