问题
I have a class that talks to a serial port and I want to mimic a Get function. The problem is, the serial port feeds in raw data which may or may not come. It also has to be processed and parsed. For example, if I want to get a Person object, the serial port class does this.
A method to send the request for a Person.
Something happens on the serial device... it might respond, it might not.
Bytes come in through an event handler that processes and parses all data, and eventually a Person object.
I want to wrap that all up into a "GetPerson" function but I'm having difficulty connecting steps in 1 and 3. Step 3 is a function that is always running and is more of a generic parser.
Can someone help me design/structure this?
Edit: Also I've tried using a class variable and checking via while statement as shown below but it doesn't check the variable during the while statement for whatever reason:
private Person person;
public Person GetPerson()
{
...
while (person == null) {}
return person;
}
回答1:
As far as I understood you need to convert EAP to TAP. In other words to wait for some data receiving event in async\await manner. You can use TaskComplettionSource<>
for that. Based on this article I tried to imagine the design appropriate solution:
public class PersonSerialPort : IDisposable
{
private readonly SerialPort port;
/// <summary>
/// Timeout in milliseconds
/// </summary>
private const int Timeout = 5000;
public PersonSerialPort()
{
// port initializing here
port = new SerialPort(/*your parameters here*/);
port.Open();
}
public async Task<Person> GetPerson()
{
// set up the task completion source
var tcs = new TaskCompletionSource<Person>();
// handler of DataReceived event of port
var handler = default(SerialDataReceivedEventHandler);
handler = (sender, eventArgs) =>
{
try
{
Person result = new Person();
// some logic for filling Person fields
// or set it null or whatever you need
// you are free to not set result and wait for next event fired too
tcs.SetResult(result);
}
finally
{
port.DataReceived -= handler;
}
};
port.DataReceived += handler;
// send request for person
port.Write("Give a person number 1");
if (await Task.WhenAny(tcs.Task, Task.Delay(Timeout)) == tcs.Task)
{
return tcs.Task.Result;
}
else
{
port.DataReceived -= handler;
throw new TimeoutException("Timeout has expired");
}
}
public void Dispose()
{
port?.Dispose();
}
}
UPD: timeout logic is added, see also this question.
Hope it helps.
来源:https://stackoverflow.com/questions/52195772/how-to-write-your-own-get-request-function-with-serial-port-data