I can pass data from a different thread to the main thread in c# by using invoke (see below code), but how do I pass data the other direction? trying to pass by reference doesnt seem to be working. Using a global doesnt work either since there are multiple threads.
Ideally I would constantly be updating the local variable in the WS_Client function and then whenever I get a request in mywebsocketserver, it triggers the handle/action which would grab that constantly updated variable and pass it to the mywebsocketserver thread. Mywebsocketserver would then send the data out via the socket.
using UnityEngine;
using WebSocketSharp;
using WebSocketSharp.Server;
using System;
public class OrientationEvent
{
public event Action<Vector3> OnOrientationUpdate;
}
public class WS_Client : MonoBehaviour
{
public static GameObject _cube;
private Action<Vector3> callback;
WebSocket ws;
[SerializeField] float speed = 50.0f;
private Vector3 _orientation;
// Start is called before the first frame update
void Start()
{
_cube = GameObject.Find("MyCube");
if (_cube == null)
{
Debug.Log("couldnt find MyCube at first");
}
WebSocketServer wssv = new WebSocketServer(8080);
Action<Vector3> callback = HandleOrientationUpdate;
MyWebSocketServer wsService = new MyWebSocketServer(callback);
wssv.AddWebSocketService<MyWebSocketServer>("/MyWebSocket", () => wsService);
wssv.Start();
}
public WS_Client(OrientationEvent orientationEvent)
{
callback = HandleOrientationUpdate;
}
private void Update()
{
_cube.transform.rotation = Quaternion.Euler(_orientation);
}
private void HandleOrientationUpdate(Vector3 orientation)
{
Debug.Log("tries to rotate mycube");
Debug.Log(orientation);
// Update the cube's orientation using the data from the event
try
{
_orientation = orientation;
Debug.Log("translates the orientation");
}
catch (Exception ex)
{
Debug.LogError("An error occurred: " + ex.Message);
}
}
}
public class MyWebSocketServer : WebSocketBehavior
{
private Action<Vector3> callback;
public MyWebSocketServer(Action<Vector3> callback)
{
this.callback = callback;
}
protected override void OnMessage(MessageEventArgs e)
{
string data = e.Data;
string[] components = data.Split(' ');
Debug.Log("splits components");
float x = float.Parse(components[0]);
float y = float.Parse(components[1]);
float z = float.Parse(components[2]);
Debug.Log("parses components");
Vector3 orientation = new Vector3(x, y, z);
// Vector3 vector = new Vector3((float)x, (float)y, (float)z);
Debug.Log("puts them in vector3");
try
{
callback?.Invoke(orientation);
Debug.Log("invokes action");
}
catch (Exception ex)
{
Debug.LogError("An error occurred: " + ex.Message);
}
}
protected override void OnOpen()
{
// Handle client connection here
Debug.Log("someone connected");
}
protected override void OnClose(CloseEventArgs e)
{
// Handle client disconnection here
Debug.Log("someone disconnected");
}
}
As said I don't see the nead to pass anything as a ref here.
I would rather do it explicit through a kind of "MainThreadDispatcher" thing like e.g.
public class WS_Client : MonoBehaviour
{
...
// Thread-safe stack
readonly ConcurrentStack<Vector3> lastReceived = new();
void Update()
{
// On the main thread handle the latest received value
if(lastReceived.TryPop(out var value)
{
_cube.transform.rotation = Quaternion.Euler(value);
// erase the others as they are outdated
lastReceived.Clear();
}
}
void HandleOrientationUpdate(Vector3 v)
{
// here only store for being handled on main thread
lastReceived.Push(v);
}
}
The same way in the other direction, here it depends a bit in your needs whether you need those reliable as a queue or again only the latest value (in that case I'd rather use a stack as above)
public class MyWebSocketServer : WebSocketBehavior
{
readonly ConcurrentQueue<Vector3> toSend = new();
readonly WaitHandle wait = new AutoResetEvent(false);
public MyWebSocketServer(...)
{
...
var sendThread = new Thread(SendThread);
sendThread.Start();
}
public void Send(Vector3 v)
{
toSend.Enqueue(v);
wait.Set();
}
private void SendThread()
{
while(true)
{
// Allows this thread to go idle => not eating unnecessary performance
wait.WaitOne();
// if using the queue go through all items, otherwise again only pop the newest value and erase the others
while(toSend.TryDequeue(out var v))
{
// However you finally send your value using ToString etc
// tbh I would prefer a byte[] based protocol over string
// it would be faster to parse and also more efficient bandwidth wise
SubmitVector(v);
}
}
}
So now you only need to call Send with the newest values to submit.
You most probably will not want to do this every frame as this will overload the network bandwidth a lot. Rather use a time interval like e.g. 0.2 seconds and interpolate at the receiver side.
I also found a simpler way to do everything, just share the variable in a shared class:
using UnityEngine;
using WebSocketSharp;
using WebSocketSharp.Server;
using System;
public class MySharedData
{
public Vector3 handPosition { get; set; }
public Quaternion handRotation { get; set; }
// public Transform handData { get; set; }
}
public class WS_Client : MonoBehaviour
{
MySharedData sharedData = new MySharedData();
WebSocket ws;
public Transform handR;
void Start()
{
handR = GameObject.Find("LeftHandAnchor").GetComponent<Transform>();
// handR = GameObject.Find("RightHandAnchor").GetComponent<Transform>();
WebSocketServer wssv = new WebSocketServer(8080); //this websocketserver sets up a place for cpus to talk
MyWebSocketServer wsService = new MyWebSocketServer(sharedData); //this is actually a type of websocketbehavior (with a callback connected to the main thread) which
wssv.AddWebSocketService<MyWebSocketServer>("/MyWebSocket", () => wsService); //then gets implemented onto the websocketserver
wssv.Start();
}
private void Update()
{
sharedData.handPosition = handR.transform.position;
sharedData.handRotation = handR.transform.rotation;
}
}
public class MyWebSocketServer : WebSocketBehavior
{
private MySharedData _sharedData;
public MyWebSocketServer(MySharedData sharedData)
{
this._sharedData = sharedData;
}
protected override void OnMessage(MessageEventArgs e)
{
try
{
string data = e.Data;
Console.WriteLine("Received message: " + e.Data);
Vector3 handPosition = _sharedData.handPosition;
Quaternion handRotation = _sharedData.handRotation;
string responseMessage = string.Format("Position:({0},{1},{2}), Rotation:({3},{4},{5})",
handPosition.x, handPosition.y, handPosition.z,
handRotation.x, handRotation.y, handRotation.z);
Debug.Log(responseMessage);
Send(responseMessage);
}
catch (Exception ex)
{
Debug.LogError("An error occurred: " + ex.Message);
}
}
protected override void OnOpen()
{
// Handle client connection here
Debug.Log("someone connected");
}
protected override void OnClose(CloseEventArgs e)
{
// Handle client disconnection here
Debug.Log("someone disconnected");
}
}
Related
After recovering my data with Firebase using a callback in GetValueAsync().ContinueWith(task..) , I would like to instantiate my prefab in order to see the list of scores for my leaderboard. But, it does nothing and I have no errors. The code simply stops in the callback UseSores as soon as it come across on a 'this' or a 'instantiate'.
public class Leaderboardmanager : MonoBehaviour
{
public GameObject rowLeardBoard;
FirebaseDB_Read read;
float positionX;
int nbRows = 10;
void Start()
{
read = (gameObject.AddComponent<FirebaseDB_Read>());
GetScorePlayer();
}
void GetScorePlayer()
{
read.GetScores(UseScores, "entries/LeaderBoard/", nbRows);
}
void UseScores(IList<FirebaseDB_Read.Score> scores)
{
Debug.Log("arrive here");
positionX = this.transform.position.y;
Debug.Log("does not arrive here");
}
}
Here is to get my data :
public class FirebaseDB_Read : MonoBehaviour
{
public class Score
{
public string UID;
public string score;
public int rank;
}
public void GetScores(Action<IList<Score>> callback, string URL_TO_SCORES, int limit)
{
DatabaseReference scoresRef = FirebaseDatabase.DefaultInstance.GetReference(URL_TO_SCORES);
scoresRef.OrderByChild("score").LimitToLast(limit).GetValueAsync().ContinueWith(task =>
{
DataSnapshot snapshot = task.Result;
IList<Score> objectsList = new List<Score> { };
int i = 1;
foreach (var childSnapshot in snapshot.Children)
{
Score score = new Score();
score.rank = i;
score.UID = childSnapshot.Child("UID").GetValue(true).ToString();
score.score = childSnapshot.Child("score").GetValue(true).ToString();
objectsList.Add(score);
i++;
}
callback(objectsList);
});
}
}
This is an often asked problem in Unity: Because you ContinueWith on a background thread!
Unity isn't thread-safe, meaning that most of the Unity API can only be used within the Unity main thread.
Firebase offers an extension specifically for Unity: ContinueWithOnMainThread which assures that the result is handled in the Unity main thread where accessing the API is valid.
scoresRef.OrderByChild("score").LimitToLast(limit).GetValueAsync().ContinueWithOnMainThread(task =>
{
...
});
As alternative you can use kind of a so called "main thread dispatcher" pattern and make sure that the callback is executed in the main thread on the receiver side. The advantage of this would be that the still expensive operations on your list are all executed on a background thread, not affecting the UI performance
scoresRef.OrderByChild("score").LimitToLast(limit).GetValueAsync().ContinueWith(task =>
{
...
});
but then on receiver side in FirebaseDB_Read
private readonly ConcurrentQueue<Action> _mainThreadActions = new ConcurrentQueue<Action>();
private void Update()
{
if(_mainThreadAction.Count > 0)
{
while(_mainThreadActions.TryDequeue(out var action))
{
action?.Invoke();
}
}
}
void GetScorePlayer()
{
read.GetScores(UseScores, "entries/LeaderBoard/", nbRows);
}
void UseScores(IList<FirebaseDB_Read.Score> scores)
{
// handle this in the next main thread update
_mainThreadActions.Enqueue(() =>
{
Debug.Log("arrive here");
positionX = this.transform.position.y;
Debug.Log("does not arrive here");
}
}
which on the offside of course introduces a little overhead for checking for any new actions in Update of course. So if you plan do use multiple of such background actions make sure to implement them in one central place in order to keep the overhead limited ;)
I'm new in Microsoft Message Queue in Windows Server, I need to push, if the EmployeeID is NULL.
The Employee Model Class is
public class Employee
{
public string EmployeeID { get; set; }
public string EmployeeName { get; set; }
}
public void ValidationProcess(Employee emp)
{
if((emp != null) || (emp.EmployeeID == null))
{
// Push into Validation Exception Queue using MSMQ
}
}
Once the Data pushed into that Validation Exception Queue, it should be processed by separate process. Every 1hr the process need to initiate and it should call the following method
public void ValidationExceptionProcess(object obj)
{
// Some Inner Process
// Log the Error
}
Kindly guide me how to create and process it.
First Step:
Install MSMQs as a windows feature on the server/pc
Then:
- Create the queue if it does not exist
- Push the message in the queue asynchronously
Useful guide
Code example for pushing and retrieving messages from msmq:
public class ExceptionMSMQ
{
private static readonly string description = "Example description";
private static readonly string path = #".\Private$\myqueue";
private static MessageQueue exceptionQueue;
public static MessageQueue ExceptionQueue
{
get
{
if (exceptionQueue == null)
{
try
{
if (MessageQueue.Exists(path))
{
exceptionQueue = new MessageQueue(path);
exceptionQueue.Label = description;
}
else
{
MessageQueue.Create(path);
exceptionQueue = new MessageQueue(path);
exceptionQueue.Label = description;
}
}
catch
{
throw;
}
finally
{
exceptionQueue.Dispose();
}
}
return exceptionQueue;
}
}
public static void PushMessage(string message)
{
ExceptionQueue.Send(message);
}
private static List<string> RetrieveMessages()
{
List<string> messages = new List<string>();
using (ExceptionQueue)
{
System.Messaging.Message[] queueMessages = ExceptionQueue.GetAllMessages();
foreach (System.Messaging.Message message in queueMessages)
{
message.Formatter = new XmlMessageFormatter(
new String[] { "System.String, mscorlib" });
string msg = message.Body.ToString();
messages.Add(msg);
}
}
return messages;
}
public static void Main(string[] args)
{
ExceptionMSMQ.PushMessage("my exception string");
}
}
An other widely used way to do that would also be to use out of the box loggers which already contains this functionality like Enterprise Library or NLog which provide easy interfaces to do that.
For retrieving messages I would recommend a separate windows service which would periodically read messages and process them. An good example on how to do that is given here: Windows service with timer
Update: Windows Service Example:
MSMQConsumerService.cs
public partial class MSMQConsumerService : ServiceBase
{
private System.Timers.Timer timer;
public MSMQConsumerService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
this.timer = new System.Timers.Timer(30000D); // 30000 milliseconds = 30 seconds
this.timer.AutoReset = true;
this.timer.Elapsed += new System.Timers.ElapsedEventHandler(this.ProcessQueueMessages);
this.timer.Start();
}
protected override void OnStop()
{
this.timer.Stop();
this.timer = null;
}
private void ProcessQueueMessages(object sender, System.Timers.ElapsedEventArgs e)
{
MessageProcessor.StartProcessing();
}
}
and the MessageProcessor.cs
public class MessageProcessor
{
public static void StartProcessing()
{
List<string> messages = ExceptionMSMQ.RetrieveMessages();
foreach(string message in messages)
{
//write message in database
}
}
}
I'm studying for an exam and I came across a question I couldn't figure out. It asks to Create a TurnOnRadio method for the Radio class. This method should remove any TV subscribers to the remote control object. I thought I could do this with just = without the += or -=. When I go to do this is says This event " RemoteControl.channelChange " can only be on the left hand side of += or -= (except when used from within the type 'Remote Control') Any help on accomplishing this task would be appreciated. Code posted below.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace RemoteControlApp2
{
class RemoteControl
{
public delegate void ChannelChanged(object remote, RemoteEventsArgs re);
public event ChannelChanged channelChange;
private int currentChannel;
public void ChangeTheCrrentChannel(int newChannel)
{
RemoteEventsArgs newRe = new RemoteEventsArgs(newChannel);
if (channelChange!=null)
{
channelChange(this, newRe);
}
}
}
class RemoteEventsArgs : EventArgs
{
public int newChannel;
public RemoteEventsArgs(int nc)
{
this.newChannel = nc;
}
}
class Television
{
private int tvChannel;
//Your code here
public void TurnOnTV(RemoteControl Remote)
{
Remote.channelChange += new RemoteControl.ChannelChanged(TVChannelChanged);
Console.WriteLine(Remote.ToString() + " is detected");
}
public void TurnOffTV(RemoteControl Remote)
{
Remote.channelChange -= new RemoteControl.ChannelChanged(TVChannelChanged);
Console.WriteLine(Remote.ToString() + " is no longer detected");
}
public void TVChannelChanged(Object Remote, RemoteEventsArgs nc)
{
Console.WriteLine("The TV channel is changed. New channel is: {0}", nc.newChannel);
}
}
class Radio
{
private int radioChannel;
//Your code here
public void TurnOnRadio(RemoteControl Remote)
{
Remote.channelChange = new RemoteControl.ChannelChanged(TVChannelChanged);
Console.WriteLine(Remote.ToString() + " is deteceted")
}
//May need to write RadioChannelChanged method
}
class Program
{
static void Main(string[] args)
{
RemoteControl rc = new RemoteControl();
Television tv = new Television();
tv.TurnOnTV(rc);
rc.ChangeTheCrrentChannel(29);
rc.ChangeTheCrrentChannel(32);
tv.TurnOffTV(rc);
Console.ReadKey();
}
}
}
I took out event from public event ChannelChanged channelchange;
So now it is public ChannelChanged channelchange;
Next I finished the radio class and TurnOnRadio method and now that event has been removed I can use = to remove all other subscriptions and now subscribes whatever channel the remote is changed to in main. Radio class code posted below.
class Radio
{
private int radioChannel;
//Your code here
public void TurnOnRadio(RemoteControl Remote)
{
Remote.channelChange = new RemoteControl.ChannelChanged(RadioChannelChanged);
//Console.WriteLine(Remote.ToString() + " is deteceted");
}
public void RadioChannelChanged(object Remote,RemoteEventsArgs re)
{
radioChannel = re.newChannel;
Console.WriteLine("Radio channel is changed. New channel is :{0}", re.newChannel);
}
//May need to write RadioChannelChanged method
}
this is basically a follow up to a previous question (Triggering an event in c# from c++ and declaring LPCWSTR). I've revised my code based on the answers and comments I have received and I solved the initial issue, which was passing the event to the GpioSetupInterruptPin from a gpio api. I don't have a lot of documentation on the api but what i'm trying to achieve is: have a form with a white label; after pressing a switch, the label turns yellow.
The problem i'm having now is the event seems to trigger as soon as it's created (the "execute" message is passed to the debug dialog and the label turns yellow) but it doesn't do anything when i toggle the switch. I was told in the last question to use WaitForSingleObject but i'm not really sure where to call it and this article only added to my confusion.
public partial class Form1 : Form
{
// P/Invoke CreateEvent and WaitForSingleObject
private void GPIO_Open() //get handle for gpio
private void GPIO_Output() //output pin declaration
private void button1_Click(object sender, EventArgs e)
{
Interrupt_Setup();
}
private void Interrupt_Setup()
{
hGPIO = GPIOapi.GpioOpenHandle(); //returns a handle to the gpio
GIPO_ON = true;
Debug.WriteLine("Driver open \n" + hGPIO);
GPIO_Output(); //set output pins
GPIO_Interrupt(Trigger); //configure interrupt
}
private void GPIO_Interrupt(string trigger)
{
bool ok;
_Main();
//INTERRUPT DECALRATION
ok = GPIOapi.GpioSetupInterruptPin(hGPIO, port6, 4, GPIOapi.INT_TRIGGER_MODE.TRIGGER_MODE_EDGE,
GPIOapi.INT_TRIGGER_POLARITY.TRIGGER_POL_HIGH_RISING, trigger, true);
Thread waitThread=new Thread(WaitForTrigger);
waitThread.Start();
if (!ok)
Debug.WriteLine("NO interrupt");
else
Debug.WriteLine("Interrupt set for:" + port6 + "04" + " at " + hGPIO);
}
public static string Trigger = "InputProcessUpdateHandler";
public static IntPtr handle = CreateEvent(IntPtr.Zero, false, false, Trigger); //used P/Invoke
private static InputProcessor inputProcessor = null;
public Color[] color =
{
Color.Orchid, Color.DarkOrchid, Color.GreenYellow, Color.CornflowerBlue, Color.SteelBlue,Color.Crimson
};
public int i = 0;
public void WaitForTrigger()
{
while(true)
{try
{
if (WaitForSingleObject(handle, 0xFFFFFFFF) == false)
{
BeginInvoke(((System.Action)(() =>label2.BackColor = color[i])));
i++;
if (i > 4)
i = 0;
}
Thread.Sleep(300);
}
catch (Exception e)
{ Debug.WriteLine("exception: " + e); }}
}
}
private void _Main()
{
inputProcessor = new InputProcessor();
ShowToggle showToggle = new ShowToggle(inputProcessor);
inputProcessor.Process(label1);
}
public class ShowToggle
{
private InputProcessor _inputProcessor = null;
public ShowToggle(InputProcessor inputProcessor)
{
_inputProcessor = inputProcessor;
_inputProcessor.updateHandledBy += InputProcessUpdateHandler;
}
private void InputProcessUpdateHandler(Label label)
{
label.BackColor = Color.Yellow;
Debug.Write("execute");
}
}
public class InputProcessor
{
public delegate void InputProcessUpdateHandler(Label label);
public event InputProcessUpdateHandler updateHandledBy = null;
public void Process(Label label)
{
if (updateHandledBy != null)
updateHandledBy(label);
}
}
If anyone could help me with this, I would be very grateful.
*** I got it working but it looks a right mess. Could anyone help me straighten it out?
You code is really confusing to me. I think what you want is something like this. Bear in mind I'm typing this into the SO text editor, so don't expect it to compile and just work - it's a guide. Consider it a step above pseudocode.
public class DeviceInterrupt
{
IntPtr m_gpio;
string m_eventName;
public event EventHandler OnInterrupt;
public DeviceInterrupt(int port)
{
// get a driver handle
m_gpio = GPIO_Open();
// generate some unique event name
m_eventName = "GPIO_evt_" + port;
// wire up the interrupt
GpioSetupInterruptPin(m_gpio, port, m_eventName, ...);
// start a listener
new Thread(EventListenerProc)
{
IsBackground = true,
Name = "gpio listener"
}
.Start();
}
public void Dispose()
{
// TODO: release the handle
}
private void EventListenerProc()
{
// create the event with the name we sent to the driver
var wh = new WaitHandle(false, m_eventName);
while (true)
{
// wait for it to get set by the driver
if (wh.WaitOne(1000))
{
// we have an interrupt
OnInterrupt.Fire(this, EventArgs.Empty);
}
}
}
}
Usage would then be something like this:
var intr = new DeviceInterrupt(4);
intr.OnInterrupt += MyHandler;
....
void MyHandler(object sender, EventArgs a)
{
Debug.WriteLine("Interrupt occurred!");
}
Note
The Compact Framework doesn't support actual named system events, so the named WaitHandle I use in my code above is not a CF-supplied WaitHandle. Instead I'm using the one from the Smart Device Framework. You could also P/Invoke to CreateEvent and WaitForSingleObject yourself.
Okay so I have a function called readSensor which you guessed it.. reads a sensor.
But the sensors usually take about 100ms to respond. So in the readSensor function I am basically just starting a timer.
On the timed event I read the serialport and get my response.
However this means that my response is in the onTimedEvent when I want it to be in the readSensor function..
Basically from the main form I want to be able to do this.
value = readSensor()
when at the minute all I can do is readSensor() and then I can see the response is coming back by displaying it in a messagebox once the timedEvent fires.
here is my code. (I have missed out loads of serialport setup and stuff but hopefully you can see the problem I am in)
I don't want to just wait in the function for 100ms though polling the timer as that will make my program slow..
I want to somehow get the response back to the readSensor function and then back to the form.
using System;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
using System.Timers;
namespace readSensor
{
public partial class readSens : UserControl
{
public readSens()
{
InitializeComponent();
}
private System.Timers.Timer rTimer;
SerialPort sp = new SerialPort();
private void setupTimer()
{
// Create a timer with a 100ms response.
rTimer = new System.Timers.Timer(100);
rTimer.SynchronizingObject = this;
// Hook up the Elapsed event for the timer.
rTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
string response = getResponse();
}
public string getResponse()
{
string status = "";
byte[] readBuffer = new byte[255];
if (sp.IsOpen)
{
if (sp.BytesToRead > 0) //there is data to read
{
int length = sp.BytesToRead;
for (int i = 0; i < length; i++)
{
readBuffer[i] = (byte)sp.ReadByte();
status = "pass";
return status;
}
}
}
public void readSensor(byte addr)
{
if (!sp.IsOpen)
{
openPort();
readSensor(addr); // calls itself again once port is opened
}
else if (sp.IsOpen)
{
rTimer.Start();
}
else
{
MessageBox.Show("Port not opened yet");
}
}
}
}
In the main form I am basically just saying
setupTimer();
readSensor();
on a button click.
I don't think you can do it without some callback mechanism. You could implement a while loop but that is not eficient as it would introduce spinning.
My advice is to implement a proper async pattern or something simple like:
ReadSensor(addr, DoSomethingWithResult);
public void DoSomethingWithResult(string result)
{
Console.WriteLine (result);
}
public partial class ReadSens : UserControl
{
private Action<string> _responseCallback;
public void ReadSensor(byte addr, Action<string> responseCallback)
{
_responseCallback = responseCallback;
// initiate timer
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
string response = getResponse();
_responseCallback(response);
}
}
Start a separate thread, then from that thread write into a queue the results back in your main thread.
class Game1
{
//We declare a queue, which is like an array that we can extract and enter data easily in a FIFO (first in, first out) style list.
Queue<string> q = new Queue<string>();
public void threadStart(object obj)
{
//We get the result of your function, while our main function is still looping and waiting.
string result = readInput()
//We tell C# that the parameter we passed in, is in fact the Game1 class passed from "t.Start"
Game1 game = (Game1)obj;
//This puts our "result" into the queue.
game.q.Enqueue(result);
}
public void start()
{
//Declares a new thread, which will run "threadStart" function.
System.Threading.Thread t = new System.Threading.Thread(threadStart);
//We start the other thread (that will run in parallel) and pass "this" as the parameter.
t.Start(this);
//We loop over and over, sleeping, whilst the other function runs at the same time. This is called "multi- threading"
while (q.Count == 0)
{
System.Threading.Thread.Sleep(10);
}
//This gets the last-entered (oldest) value from the queue q.
string result = q.Deque();
}
}
So this sets off a thread to get the result, and then in my version, polls the queue for a while until the results come back, but in yours could do a bunch of stuff, as long as you check the queue every now and again for new data.
Edit: Added commenting to hopefully alleviate some of your questions.
Could be this approach a valid solution for you? I think you only are using Timer to wait the serialPort to be open, but it can be self-controlled with raising event.
public class SensorReader
{
private Sensor sensor;
private string lastResponse;
public SensorReader(SerialPort serialPort)
{
this.serialPort = aSerialPort.
this.sensor = new Sensor(serialPort);
this.sensor.PortOpen += PortOpenEventHandler(OnPortOpen);
}
private void OnPortOpen()
{
this.ReadPort();
}
public string ReadPort(byte address)
{
if (!this.sensor.IsOpen)
{
this.sensor.OpenPort();
this.lastResponse = "The serial port doesn't respond... yet!";
}
else
{
// Read response at this point.
this.lastResponse = this.GetResponse();
}
return this.lastResponse;
}
}
public class Sensor
{
private SerialPort serialPort;
public Sensor(SerialPort aSerialPort)
{
this.serialPort = aSerialPort;
}
public bool IsOpen
{
get { return this.serialPort.IsOpen; }
}
public delegate void PortOpenEventHandler(object sender, EventArgs e);
public event PortOpenEventHandler PortOpen;
public void OpenPort()
{
// Open port here...
// ... and throw the PortOpen event.
if (this.PortOpen != null)
{
this.PortOpen(this, EventArgs.Empty);
}
}
}