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();
}
}
Related
I have a serial port connection to weighing indicator(display) that connecting to the scale.
The device B has 3 state:
No Power(Plug off)
Power On(Plug on but not power up)
Display Ready(after press power up)
On any state, I can connect to serial port but I can only get DataReceived event when display ready state (#3). If on certain timeout no value from DataReceived, I need to trigger the screen to alert the user that device is not on ready state. When user press Power On and after the display is ready, then I can revoke the trigger so the screen can continue when DataReceived arrived.
I have try using ManualResetEvent based from what I found:
ManualResetEvent DataReceivedEvent = new ManualResetEvent(false);
private void Open_Click(object sender, RoutedEventArgs e)
{
// All the port initialization
_serialPort.Open();
TimeSpan waitTime = TimeSpan.FromMilliseconds(5000);
bool noData = DataReceivedEvent.WaitOne(waitTime);
}
private void SerialPortOnDataReceived(object sender, SerialDataReceivedEventArgs serialDataReceivedEventArgs)
{
SerialPort sp = (SerialPort)sender;
DataReceivedEvent.Set();
}
The MRE seems promising since I can wait, Set the event so I know the data is received, and return false when WaitOne not Set but it freezing my UI and it just run one time after serial port is open.
Is it possible to put ManualResetEvent under thread to keep loop and wait without freezing UI? I try to search for it but I cannot find it.
Based from #kunif suggestion, I have come to use DateTime as a benchmark to see if the device and display is disconnected. The connection status return accordingly on device turn off, display power up and display ready.
private DateTime LastBufferTime;
private bool Running { get; set; } = true;
private void Open_Click(object sender, RoutedEventArgs e)
{
Running = true;
LastBufferTime = DateTime.Now;
_serialPort.DataReceived += SerialPortOnDataReceived;
_serialPort.Open();
Task.Run(() => ThreadTimer());
}
private async void ThreadTimer()
{
while (Running)
{
if ((DateTime.Now - LastBufferTime).TotalSeconds > 5)
{
// No response from DataReceived
}
else
{
// Response from DataReceived
}
}
}
private void SerialPortOnDataReceived(object sender, SerialDataReceivedEventArgs serialDataReceivedEventArgs)
{
SerialPort sp = (SerialPort)sender;
LastBufferTime = DateTime.Now;
}
private void Stop_Click(object sender, RoutedEventArgs e)
{
if (_serialPort != null)
{
if (_serialPort.IsOpen)
{
_serialPort.Close();
}
_serialPort.DataReceived -= SerialPortOnDataReceived;
_serialPort.Dispose();
Running = false;
}
}
I'm trying to connect to arduino with C# program in windows forms.
I can send data from c# to the arduino but I want to get data from the arduino to the C# program.
I've tried the SerialDataReceivedEventHandler, but I don't get the data after the form is built...
what can I do?
public Form1()
{
InitializeComponent();
Init();
}//end form 1
private void Init()
{
try
{
arduinoPort = new SerialPort();
arduinoPort.BaudRate = 9600;
arduinoPort.PortName = "COM4";
arduinoPort.Handshake = Handshake.None;
arduinoPort.RtsEnable = true;//request to send true
arduinoPort.DtrEnable = true;//arduino can send messages to the c# program
arduinoPort.DataReceived += new SerialDataReceivedEventHandler(GetFromArduino);
arduinoPort.Open();
}//end try
catch (Exception ex) { MessageBox.Show(ex.Message); }
}//end init
private void GetFromArduino(object sender, SerialDataReceivedEventArgs e)
{
//string arduinoInputString = arduinoPort.ReadLine();
//Invoke(new Action(() => label1.Text = arduinoInputString));
MessageBox.Show("does it work?");
}//end get from arduino
MessageBox.Show isn't going to work from the DataReceived event
SerialPort.DataReceived Event
The DataReceived event is raised on a secondary thread when data is
received from the SerialPort object. Because this event is raised on a
secondary thread, and not the main thread, attempting to modify some
elements in the main thread, such as UI elements, could raise a
threading exception. If it is necessary to modify elements in the main
Form or Control, post change requests back using Invoke, which will do
the work on the proper thread
at best you need to do something like this
this.Invoke(new Action(() => { MessageBox.Show(this, "text"); }));
However, if you really want to know if that event is fired, then use a break-point
Using Breakpoints
Lastly, if the event isnt fired, then you will have to consult the documentation for the device for the appropriate configuration of the device and the serial port.
Make some handler method, for example
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort comm = (SerialPort)sender;
string incoming_Data = comm.ReadExisting();
this.BeginInvoke((MethodInvoker)delegate()
{
Console.WriteLine(incoming_Data + "\n");
});
}
Then, make this method to the subscriber of DataRecieved event
arduinoPort.DataReceived += DataReceivedHandler;
Source: https://social.msdn.microsoft.com/Forums/vstudio/en-US/bd8f7ac8-67d5-4eb5-b679-c595f9c7536d/how-to-print-out-text-from-serialport-datareceived-event?forum=netfxbcl
I don't know if it matters, but this what i did and it solved:
1. in the setup function of the arduino program i had a loop that ran on all the pins, now i specified it to the pins i really use.
2. i don't open the port in the form.cs, but in the program.cs and run a while loop that just check if the port is open. then, it gets the data without disturbing the form to run...
**in the Program.cs:**
public static SerialPort arduinoPort { get; set; }
public static string arduinoInputString { get; set; }
[STAThread]
static void Main()
{
arduinoPort = new SerialPort();
try
{
arduinoPort = new SerialPort();
arduinoPort.BaudRate = 250000;
arduinoPort.PortName = "COM4";
//arduinoPort.Handshake = Handshake.None;
//arduinoPort.RtsEnable = true;//request to send true
//arduinoPort.DtrEnable = true;//arduino can send messages to the c# program
////arduinoPort.DataReceived += new SerialDataReceivedEventHandler(GetFromArduino);
arduinoPort.DataReceived += DataReceivedHandler;
arduinoPort.Open();
}//end try
catch (Exception ex) { Console.WriteLine((ex.Message)); }
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
while (arduinoPort.IsOpen)//read data if the port is open
{
}
}//end main
static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort comm = (SerialPort)sender;
arduinoInputString = comm.ReadExisting();
Form1.label1.Text = arduinoInputString;//label to show the input string from arduino
}//end get data from arduino
I am using a websocket sharp dll in my windows application to get messages from a GDAX server. Everything is working fine so far - the messages are coming and i am processing them. The point where I am stuck is when the messages stops coming. At least I don't find anything in the WebSocket.OnMessage Event(https://github.com/sta/websocket-sharp) that can help me in tracking when the messages are stopped (I tried emitonping also)
Now the messages I received have a message type 'Heartbeat' which is sent every second. I want to add a separate timer control to check if the heartbeat messages are coming every second or not and if it stops coming then I will need to reconnect the server again. But since nothing happens when the messages stops coming how do i track it, where should I put the timer code to check when heartbeat messages stops coming?
I hope I could explain the situation wherein I am struck. If someone is eager to help me and needs more inputs please let me know.
Update
private void _3_Load(object sender, EventArgs e)
{
ConnectAndGetWebsocketFeedMessages();
}
public delegate void WSOpen(string text);
public delegate void WSMessage(string message);
public delegate void WSError(string text);
public delegate void WSClose(string text);
private static string _endPoint = "wss://ws-feed.gdax.com";
WebSocket ws = new WebSocket(_endPoint);
private bool IsConnected { get; set; }
private string ProductId { get; set; }
string productId = "LTC-EUR";
ConcurrentQueue<string> concurrentQueue = new ConcurrentQueue<string>();
public void SetWebSocketSharpEvents()
{
ws.Log.Level = LogLevel.Trace;
ws.OnOpen += (sender, e) =>
{
IsConnected = true;
OnWSOpen("Connection Status :: Connected *********");
};
ws.EmitOnPing = true;
ws.OnMessage += (sender, e) =>
{
if (e.IsPing)
{
OnWSMessage("ping received");
}
else
{
OnWSMessage(e.Data);
}
};
ws.OnError += (sender, e) =>
{
IsConnected = false;
OnWSError(e.Message); //An exception has occurred during an OnMessage event. An error has occurred in closing the connection.
if (ws.IsAlive)
ws.Close();
};
ws.OnClose += (sender, e) =>
{
IsConnected = false;
OnWSClose("Close");
};
ws.ConnectAsync();
}
private void ConnectAndGetWebsocketFeedMessages()
{
SetWebSocketSharpEvents();
}
private void SubscribeProduct(string sProductID)
{
if (IsConnected)
{
ProductId = sProductID;
string data = "{\"type\": \"subscribe\", \"product_ids\": [\"" + sProductID + "\"]}";
ws.Send(data);
ws.Send("{\"type\": \"heartbeat\", \"on\": true}");
}
}
void OnWSOpen(string text)
{
SubscribeProduct(productId);
timer1.Interval = 1000;
timer1.Tick += timer1_Tick;
timer1.Start();
}
DateTime lastHeartbeatTime = DateTime.MinValue;
bool isTimerStart = false;
void OnWSMessage(string message)
{
concurrentQueue.Enqueue(message);
SaveHeartbeatMessageTime(message);
ProcessMessage(message);
}
private void SaveHeartbeatMessageTime(string jsonString)
{
var jToken = JToken.Parse(jsonString);
var typeToken = jToken["type"];
var type = typeToken.ToString();
if (type == "heartbeat")
{
lastHeartbeatTime = DateTime.Now;
this.Invoke(new MethodInvoker(delegate()
{
lbllastheartbeat.Text = lastHeartbeatTime.ToLongTimeString();
}));
}
}
private void ProcessMessage(string message) { }
void OnWSError(string text) { }
void OnWSClose(string text) { }
bool isMessagesReceived = false;
private void timer1_Tick(object sender, EventArgs e) // it stops working as soon as lbllastheartbeat gets some value
{
DateTime currentTime = DateTime.Now;
TimeSpan duration = currentTime.Subtract(lastHeartbeatTime);
this.Invoke(new MethodInvoker(delegate()
{
lblNow.Text = currentTime.ToLongTimeString();
}));
if (Int16.Parse(duration.ToString("ss")) > 1)
{
// reconnect here
}
}
Edit
I am using windows form timer control and it keeps on calling timer1_Tick method and does not call OnWSMessage method. How do I ensure that both run parallel and if any message is missed or the message stops coming then it reconnects?
Edit2
The solutions provided below suggests to add the timer functionality in onMessage event but what will happen if I do not receive messages? If the messages are not received then the code does not do anything. I have taken a global variable and whenever a message comes it adds the time in that variable. Now I want to run a separate timer control which will check whether there is anything in that variable and if its value i.e difference of seconds is more than 1 then do something else keep on checking.
Is there anyone who can look into this and advise please.
Update2: I still want to do this with windows.timer control and not threading.timer. I have taken two labels in my windows app, lbllastheartbeat (to show the time when heartbeat message is received) and lblNow (to show the current time when timer is called).
Requirement - My timer will check if any heartbeat message is missed and that is done through the 'lastHeartbeatTime' variable which stores the time when the heartbeat message is received.
I would appreciate if anyone can review my code and suggest what or where I am doing wrong.
The answer has already been given - you need to start timer which will fire after your timeout period when you receive message, and reset that timer every time you receive message. But it seems you want code example, so here it is (with comments):
System.Threading.Timer _timeoutTimer;
private readonly object _timeoutTimerLock = new object();
private void ResetTimeoutTimer() {
// if you are sure you will never access this from multiple threads at the same time - remove lock
lock (_timeoutTimerLock) {
// initialize or reset the timer to fire once, after 2 seconds
if (_timeoutTimer == null)
_timeoutTimer = new System.Threading.Timer(ReconnectAfterTimeout, null, TimeSpan.FromSeconds(2), Timeout.InfiniteTimeSpan);
else
_timeoutTimer.Change(TimeSpan.FromSeconds(2), Timeout.InfiniteTimeSpan);
}
}
private void StopTimeoutTimer() {
// if you are sure you will never access this from multiple threads at the same time - remove lock
lock (_timeoutTimerLock) {
if (_timeoutTimer != null)
_timeoutTimer.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
}
}
private void ReconnectAfterTimeout(object state) {
// reconnect here
}
public void SetWebSocketSharpEvents() {
ws.Log.Level = LogLevel.Trace;
ws.OnOpen += (sender, e) => {
// start timer here so that if you don't get first message after 2 seconds - reconnect
ResetTimeoutTimer();
IsConnected = true;
OnWSOpen("Connection Status :: Connected *********");
};
ws.EmitOnPing = true;
ws.OnMessage += (sender, e) => {
// and here
ResetTimeoutTimer();
if (e.IsPing) {
OnWSMessage("ping received");
}
else {
OnWSMessage(e.Data);
}
};
ws.OnError += (sender, e) => {
// stop it here
StopTimeoutTimer();
IsConnected = false;
OnWSError(e.Message); //An exception has occurred during an OnMessage event. An error has occurred in closing the connection.
if (ws.IsAlive)
ws.Close();
};
ws.OnClose += (sender, e) => {
// and here
StopTimeoutTimer();
IsConnected = false;
OnWSClose("Close");
};
ws.ConnectAsync();
}
from your question what i understand is , your message is sending after every seconds , but the problem is only when it stop you want to know and run it again, if it like that , you just apply timer and check for every seconds if the message not sent after a second or more (check sentMessage() Method set a boolean if message sent it should give true otherwise false) , than give the command to reconnect the server again .
This is the code I use to record an audio file:
internal class AudioRecorder
{
public WaveIn waveSource = null;
public WaveFileWriter waveFile = null;
public string RECORDING_PATH;
public AudioRecorder(string fileName)
{
RECORDING_PATH = fileName;
}
public void Start()
{
waveSource = new WaveIn();
waveSource.WaveFormat = new WaveFormat(44100, 1);
waveSource.DeviceNumber = 0;
waveSource.DataAvailable += new EventHandler<WaveInEventArgs>(waveSource_DataAvailable);
waveSource.RecordingStopped += new EventHandler<StoppedEventArgs>(waveSource_RecordingStopped);
waveFile = new WaveFileWriter(RECORDING_PATH, waveSource.WaveFormat);
System.Timers.Timer t = new System.Timers.Timer(30000);
t.Elapsed += new ElapsedEventHandler(Stop);
waveSource.StartRecording();
t.Start();
}
private void Stop(object sender, ElapsedEventArgs args)
{
waveSource.StopRecording();
}
private void waveSource_DataAvailable(object sender, WaveInEventArgs e)
{
if (waveFile != null)
{
waveFile.Write(e.Buffer, 0, e.BytesRecorded);
waveFile.Flush();
}
}
private void waveSource_RecordingStopped(object sender, StoppedEventArgs e)
{
if (waveSource != null)
{
waveSource.Dispose();
waveSource = null;
}
if (waveFile != null)
{
waveFile.Dispose();
waveFile = null;
}
}
}
In the main method I do:
AudioRecorder r = new AudioRecorder(dialog.FileName);
r.Start();
FileInfo file = new FileInfo(r.RECORDING_PATH);
// Do somehting with the recorded audio //
The problem is that when I do r.Start() the thread does not block and keeps running. So I get a corrupt file error. When I try things like Thread.Sleep to keep the thread waiting until recording finishes, this time the AudioRecorder code does not work well (i.e. recording never finishes).
Any ideas about what should I do to correctly wait the recording to finish so that I can safely use the recorded file ?
If you want to record for 30 seconds exactly, just call StopRecording in the DataAvailable event handler once you have enough data. There is absolutely no need for a complicated threading strategy. I do exactly this in the open source .NET voice recorder application.
Dispose the WaveFileWriter in the RecordingStopped event.
If you absolutely must have a blocking call, then use WaveInEvent, and wait on an event which is set in the RecordingStopped handler, as suggested by Rene. By using WaveInEvent, you remove the need for windows message pump to be operational.
You use a ManualResetEvent to wait for the Stop event to be called, giving other threads a change to proceed.
I've only added the new bits...
internal class AudioRecorder
{
private ManualResetEvent mre = new ManualResetEvent(false);
public void Start()
{
t.Start();
while (!mre.WaitOne(200))
{
// NAudio requires the windows message pump to be operational
// this works but you better raise an event
Application.DoEvents();
}
}
private void Stop(object sender, ElapsedEventArgs args)
{
// better: raise an event from here!
waveSource.StopRecording();
}
private void waveSource_RecordingStopped(object sender, EventArgs e)
{
/// ... your code here
mre.Set(); // signal thread we're done!
}
It is good idea to avoid any multi-threaded code if it is not required and Mark's answer is explaining this perfectly.
However, if you are writing a windows application and the requirement is to record 30 seconds than it is a must not to block a main thread in waiting (for 30 seconds). The new async C# feature can be very handy here. It will allow you to keep code logic straightforward and implement waiting in a very efficient way.
I have modified your code slightly to show how the async feature can be used in this case.
Here is the Record method:
public async Task RecordFixedTime(TimeSpan span)
{
waveSource = new WaveIn {WaveFormat = new WaveFormat(44100, 1), DeviceNumber = 0};
waveSource.DataAvailable += new EventHandler<WaveInEventArgs>(waveSource_DataAvailable);
waveSource.RecordingStopped += new EventHandler<StoppedEventArgs>(waveSource_RecordingStopped);
waveFile = new WaveFileWriter(RECORDING_PATH, waveSource.WaveFormat);
waveSource.StartRecording();
await Task.Delay(span);
waveSource.StopRecording();
}
Example of using Record from click handler of WPF app:
private async void btnRecord_Click(object sender, RoutedEventArgs e)
{
try
{
btnRecord.IsEnabled = false;
var fileName = Path.GetTempFileName() + ".wav";
var recorder = new AudioRecorder(fileName);
await recorder.RecordFixedTime(TimeSpan.FromSeconds(5));
Process.Start(fileName);
}
finally
{
btnRecord.IsEnabled = true;
}
}
However, you have to watch out for timing here. Task.Delay does not guarantee that it will continue execution after the exact specified time span. You might get records slightly longer than is required.
I have a TCP server running which spits out messages of 2 bytes at regular intervals.
I'm trying to create a client side form which connects to the server and continuously reads from the stream until I click a disconnect button on the form.
So far the client works fine except that I cannot disconnect. I set the CancellationPending to true but it seems to reset back to false before the dowork method gets a chance to set e.Cancel.
I'm also sure there must be a more acceptable way of continuously reading the stream and writing to the form - at the moment I am calling RunWorkerAsync within the Worker Completed method to achieve the loop!
private void Disconnect()
{
commsWorker1.CancelAsync();
}
private void ReadFromStream()
{
try
{
commsWorker1.RunWorkerAsync();
}
catch (Exception ex)
{
writeToBox("Error: " + ex.Message);
}
}
//background worker dowork method
private void BackGroundGetServerData(object sender, DoWorkEventArgs e)
{
if (true == commsWorker1.CancellationPending)
{
e.Cancel = true;
}
else
{
Byte[] dataArray = new Byte[2];
try
{
_DataStream.Read(dataArray, 0, 2);
String reply = System.Text.Encoding.ASCII.GetString(dataArray);
e.Result = reply;
}
catch (Exception ex)
{
}
}
}
//background worker workercompleted method
private void BackGroundDisplayMessages(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
//close connection here
}
else
{
writeToBox((String)e.Result);
commsWorker1.RunWorkerAsync();
}
}
}
}
Can't you just loop inside the background worker method?
private void BackGroundGetServerData(object sender, DoWorkEventArgs e)
{
while(true)
{
Byte[] dataArray = new Byte[2];
try
{
_DataStream.Read(dataArray, 0, 2);
String reply = System.Text.Encoding.ASCII.GetString(dataArray);
e.Result = reply;
}
catch (Exception ex)
{
return;
}
}
}
Then upon disconnect simply close the socket. This will cause the Exception to be thrown in the while loop and you can exit gracefully through the catch block.
Edit: Then you can update the GUI from the loop after each message is read. Make sure the handle to the control you are updating is available (assuming it's called box):
delegate void updateDelegate(String p);
private void BackGroundGetServerData(object sender, DoWorkEventArgs e)
{
while(true)
{
Byte[] dataArray = new Byte[2];
try
{
_DataStream.Read(dataArray, 0, 2);
String reply = System.Text.Encoding.ASCII.GetString(dataArray);
box.BeginInvoke(new updateDelegate(writeToBox), reply);
}
catch (Exception ex)
{
return;
}
}
}
BeginInvoke is required in this case because you are trying to update the GUI from another thread, which is not allowed. This method forwards the update to the GUI thread.
It seems that you are invoking RunWorkerAsync() in the worker complete method and that resets your CancellationPending prop. I think you can try to fix this by adding to Disconnect() method some disconnectFlag = true; and in WorkerComplete method you should add:
if (e.Cancelled || disconnectFlag)
{
disconnectFlag = false;
//close connection here
} else ...