New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.
Already on GitHub? Sign in to your account
reader.listen #3
Comments
Ok, I will finish it tomorrow(UTC+8) |
done, maybe... I don't have time to test it today. Future<StreamSubscription> readBytesOnListen(
int bytesSize, Function(Uint8List value) onData,
{required Function() onListen}){
/// implement
} |
I will add listen to write and test it in the next few days |
@FengChendian readBytesOnListen is not working for me. Is it not the function to constantly listen to receiving data ? |
Yeah... The function was called only once. Because I close the subscription when bytes was read or timeout. It is not a constant listener. |
@sabin26 Maybe I should call it readBytesWithFunction.. .I achieve it for doing something when reading bytes. I think I should create an API for constantly listen to receiving data... |
@FengChendian Yes. If there is an API to listen receiving data then this package would cover most use cases. I am blocked in my project because of the unavailability of this function. I hope the API to be implemented soon. |
@FengChendian I looked into event driven method for being notified when the data arrives and found the use of 'WaitCommEvent'. I have no experience in win32 else I would have written the code and submitted a PR to this repository. I am going to place some links that might be useful. https://docs.microsoft.com/en-us/previous-versions/ms810467(v=msdn.10)?redirectedfrom=MSDN#serial-status I can confirm that, the required classes are available on the flutter win32 packages. If your are able to understand and write code for its API, it would be really appreciated. |
I can try this tomorrow. Now I don't have any Serial Port device, because I can't go back to school due to the delta COVID-19 in Nanjing, China. So progress is slow. I will try to configure the virtual serial port to implement these method. |
@sabin26 I didn't find CreateEvent function in win32 flutter package. So I violently achieved constantly listen function... 馃槀 Here is source code: void readBytesOnlisten(int bytesSize, Function(Uint8List value) onData) {
if (_timer != null) {
_timer!.cancel();
}
_timer = Timer.periodic(Duration(milliseconds: 20), (timer) {
readBytes(bytesSize).then((value) {
if (value.isEmpty) {
return;
}
onData(value);
});
});
} |
@sabin26 done, you can test it using port.readBytesOnListen(8, (value) => print(value)); Print will be called when receiving value/ |
I saw this method but with byteSize 1 through your other repository: SerialPortTool. You did the exact same thing there. I will test it out. |
Yeah... I use timer to implement read listen in my small project. Beacuse I don鈥檛 know whether Stream has a suitable API. |
@FengChendian Is it the bytesize or the timer of 20ms, I am getting wrong data sometimes. Should I drop my bytesize to 1 to refresh fast ? |
Through my usage, I found that waiting for the bytes to arrive from the event as done in C++ or C# is the perfect way to solve the issue rather than looping the reading method continuously. So, I will be shifting my work to c# from flutter at-least for my current desktop project. |
@sabin26 Now library 0.2.2 API is void readBytesOnListen(int bytesSize, Function(Uint8List value) onData, {void onBefore()?, Duration? duration}) You can set proper dutation using parameters duration. About wrong data, I can't reproduce this bug. It works fine on my machine. d0.00 may be wrong data, too. And about reading method continuously, win32 package doesn't supports CreatEvent to implement event. I will comment on win32 package. |
I saw that your issue made a contributor submit a PR to win32 repo on adding the CreateEvent method. I will follow up on that and I hope when win32 will merge the PR, this package will update the readBytesOnListen API based on the new method. You can test out the PR and be ready to update when win32 will support it. |
@sabin26 I saw it too. I will test out the PR and try to implement wait event to get data properly. I hope that win32 package will merge the PR later. Then I will update this package to support new API. |
@FengChendian The PR has been merged in the github repo of win32 package. |
I saw it. I will update this package if I have time in next few days. |
@FengChendian Any updates ? |
@sabin26 Sorry... I am busy with my subject this week. I will try to update tomorrow. |
@sabin26 Done in version 0.3.1. |
@FengChendian (My expectation is shown below in the form of an example)
The above example is just a representation of what I want to achieve with this readBytesOnListen function. The implementation can be a bit different. Here is the screenshot of the first comment on the issue. It says implementing a stream for read data. I believe the 2nd line represents my scenario. And I am sorry if my previous comments could not make you clear on what the issue was. |
@sabin26 I use And most importantly, UI blocking is due to Because this function will block until signal coming or error in FILE_ATTRIBUTE_NORMAL mode. It's a bad Windows API. Or you just want an event? If |
@sabin26 I re-read win32 Docs. I know the solution about function blocking when calling WaitCommEvent. But win32 package does't publish 2.2.6 version which contains Here is the C++ code which using Async Mode. If your network is good, you can compare the way I use win32 and translate these codes into dart code... #include <windows.h>
#include <tchar.h>
#include <assert.h>
#include <stdio.h>
using namespace std;
void test()
{
DWORD dwCommEvent;
DWORD dwRead;
char chRead = 0;
OVERLAPPED o;
HANDLE hCom;
hCom = CreateFile(TEXT("\\\\.\\COM6"),
GENERIC_READ | GENERIC_WRITE,
0, // exclusive access
NULL, // default security attributes
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL
);
if (hCom == INVALID_HANDLE_VALUE)
{
// Handle the error.
printf("CreateFile failed with error %d.\n", GetLastError());
return;
}
DCB dcbSerialParameters = { 0 };
if (!GetCommState(hCom, &dcbSerialParameters))
{
printf("Failed to get current serial parameters\n");
}
else
{
dcbSerialParameters.BaudRate = CBR_115200;
dcbSerialParameters.ByteSize = 8;
dcbSerialParameters.StopBits = ONESTOPBIT;
dcbSerialParameters.Parity = NOPARITY;
dcbSerialParameters.fDtrControl = DTR_CONTROL_DISABLE;
if (!SetCommState(hCom, &dcbSerialParameters))
{
printf("Could not set serial port parameters\n");
}
else
{
PurgeComm(hCom, PURGE_RXCLEAR | PURGE_TXCLEAR);
Sleep(100);
}
}
if (!SetCommMask(hCom, EV_RXCHAR))
{
return;
}
o.hEvent = CreateEvent(
NULL, // default security attributes
TRUE, // manual-reset event
FALSE, // not signaled
NULL // no name
);
// Initialize the rest of the OVERLAPPED structure to zero.
o.Internal = 0;
o.InternalHigh = 0;
o.Offset = 0;
o.OffsetHigh = 0;
assert(o.hEvent);
// Error setting communications event mask.
for (; ; )
{
if (WaitCommEvent(hCom, &dwCommEvent, &o))
{
if (ReadFile(hCom, &chRead, 1, &dwRead, NULL))
{
cout << "Read" << chRead << endl;
}
else
{
cout << "error, read:" << chRead << endl;
continue;
}
}
else
{
if (GetLastError() == ERROR_IO_PENDING)
{
cout << " ERROR_IO_PENDING" << endl;
if (WaitForSingleObject(o.hEvent, 500) == WAIT_OBJECT_0)
{
ReadFile(hCom, &chRead, 1, &dwRead, &o);
cout << "Read:" << chRead << endl;
}
}
ResetEvent(o.hEvent);
continue;
}
}
}
int main()
{
cout << "Hello CMake." << endl;
test();
return 0;
}
|
@FengChendian The package win32 has been updated to version 2.2.6 that contains CreateEvent function. I tried to modify void readBytesOnListen(int bytesSize, Function(Uint8List value) onData, {void onBefore()?}) async {
Uint8List uint8list;
if (SetCommMask(handler!, 0x0001) == 0) {
throw Exception("SetCommMask EV_RXCHAR failed");
}
if (onBefore != null) {
onBefore();
}
await _computer.turnOn();
OVERLAPPED o = OVERLAPPED();
o.hEvent = CreateEvent(
nullptr, // default security attributes
TRUE, // manual-reset event
FALSE, // not signaled
nullptr // no name
);
// Initialize the rest of the OVERLAPPED structure to zero.
o.Internal = 0;
o.InternalHigh = 0;
assert(o.hEvent != 0);
for (;;) {
if (await _computer.compute(wait, param: handler) == 1) {
uint8list = await _read(bytesSize);
if (uint8list.isNotEmpty) {
onData(uint8list);
}
} else {
if (GetLastError() == ERROR_IO_PENDING) {
if (WaitForSingleObject(o.hEvent, 500) == WAIT_OBJECT_0) {
uint8list = await _read(bytesSize);
if (uint8list.isNotEmpty) {
onData(uint8list);
}
}
}
ResetEvent(o.hEvent);
}
}
} But there are 3 objects that give the error as they are not available on win32 package (or you could say that I could not find it). ERROR_IO_PENDING
WAIT_OBJECT_0
ResetEvent() I removed the below given lines as they were also not available. o.Offset = 0;
o.OffsetHigh = 0; I edited the SerialPort.Open function to openFile using FILE_FLAG_OVERLAPPED. void open() {
if (_isOpened == false) {
handler = CreateFile(_portNameUtf16, GENERIC_READ | GENERIC_WRITE, 0,
nullptr, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (handler == INVALID_HANDLE_VALUE) {
final lastError = GetLastError();
if (lastError == ERROR_FILE_NOT_FOUND) {
throw Exception(_portNameUtf16.toDartString() + "is not available");
} else {
throw Exception('Last error is $lastError');
}
}
_setCommState();
_setCommTimeouts();
_isOpened = true;
} else {
throw Exception('Port is opened');
}
} |
Although the |
I have tested compute function in version 0.4.1 on GitHub. There are no UI blocking when loop. Because most of time will be used to wait Event, which is isolated. But if you use WaitEvent function in FILE_ATTRIBUTE_NORMAL, you can't close handle while waiting event. That's a serious problem. Maybe there are some problems which I can't realize. But I think I can't find better solution for constantly listen now. Because of WaitEvent function, I can't use stream or timer. Data isn't iterable or periodical. And timeout is infinite in this function. I think another solution is using overlapped mode in I/O. Win32 package has published version 2.2.6 today. I will test it and using stream or timer to implement listen. |
@sabin26 Done. I think it is no UI-blocking or stuck problem. There are two ways to change onlisten function. port.readOnListenFunction = (value) {
print(value);
};
Future.delayed(Duration(seconds: 4)).then(
(value) => port.readBytesOnListen(2, (value) => print('object$value'))); But I want to remind you that win32 package doesn't have ResetEvent(). Auto-reset event is a little strange in my computer. ReadByteSize must be less or equal to receiced buffer size. Otherwise, ReadFile function always gives me zero data read. I don't know why. And I will open an issue in win32 package to request ResetEvent(). |
If only we could do main() {
.....
serialPort.OnDataReceived = OnDataReceivedFromSerialDevice;
serialPort.Open();
}
OnDataReceivedFromSerialDevice() {
// showing byte[] instead of Uint8List only for demo
byte[] buffer = new byte[_serialPort.BytesToRead]; // this will be very helpful
_serialPort.Read(buffer, 0, buffer.Length); // read the data ourselves
......
do something with the data
......
} Here, the @FengChendian What do you think ? |
@sabin26 Do you mean I just let people know the number of BytesToRead and give a function named OnDataReceivedFromSerialDevice which doesn't have read function inside? It's a good idea. However, there are some technical issues or win32 bug. About getting the precise number of BytesToRead in Dart. void test()
{
DWORD dwCommEvent;
DWORD dwRead;
DWORD errors;
COMSTAT status;
OVERLAPPED o;
HANDLE hCom;
hCom = CreateFile(TEXT("\\\\.\\COM6"),
GENERIC_READ | GENERIC_WRITE,
0, // exclusive access
NULL, // default security attributes
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL
);
if (hCom == INVALID_HANDLE_VALUE)
{
// Handle the error.
printf("CreateFile failed with error %d.\n", GetLastError());
return;
}
DCB dcbSerialParameters = { 0 };
if (!GetCommState(hCom, &dcbSerialParameters))
{
printf("Failed to get current serial parameters\n");
}
else
{
dcbSerialParameters.BaudRate = CBR_115200;
dcbSerialParameters.ByteSize = 8;
dcbSerialParameters.StopBits = ONESTOPBIT;
dcbSerialParameters.Parity = NOPARITY;
dcbSerialParameters.fDtrControl = DTR_CONTROL_DISABLE;
if (!SetCommState(hCom, &dcbSerialParameters))
{
printf("Could not set serial port parameters\n");
}
else
{
PurgeComm(hCom, PURGE_RXCLEAR | PURGE_TXCLEAR);
Sleep(100);
}
}
if (!SetCommMask(hCom, EV_RXCHAR))
{
return;
}
o.hEvent = CreateEvent(
NULL, // default security attributes
TRUE, // manual-reset event
FALSE, // not signaled
NULL // no name
);
// Initialize the rest of the OVERLAPPED structure to zero.
o.Internal = 0;
o.InternalHigh = 0;
o.Offset = 0;
o.OffsetHigh = 0;
assert(o.hEvent);
// Error setting communications event mask.
for (; ; )
{
char chRead[20] = { '\0' };
if (WaitCommEvent(hCom, &dwCommEvent, &o))
{
if (ReadFile(hCom, &chRead, 4, &dwRead, NULL))
{
cout << "Read" << chRead << endl;
}
else
{
cout << "error, read:" << chRead << endl;
continue;
}
}
else
{
if (GetLastError() == ERROR_IO_PENDING)
{
cout << " ERROR_IO_PENDING" << endl;
if (WaitForSingleObject(o.hEvent, 500) == WAIT_OBJECT_0)
{
ClearCommError(hCom, &errors, &status);
cout << "Que" << status.cbInQue << endl;
ReadFile(hCom, &chRead, status.cbInQue, &dwRead, &o);
cout << "Read:" << dwRead << endl;
}
}
ResetEvent(o.hEvent);
continue;
}
}
} But in dart, you will find code can't respond correctly when bytesToRead(cbInQue) is 1... Data will be zero sometimes. Or you will get more data in a few seconds. Or data is right. 2,3,4,5 is fine. /// look up I/O event and read data using stream
Stream<Uint8List> _lookUpEvent(Duration interval) async* {
int event = 0;
Uint8List data;
PurgeComm(handler!, PURGE_RXCLEAR | PURGE_TXCLEAR);
while (true) {
await Future.delayed(interval);
event = WaitCommEvent(handler!, _dwCommEvent, _over);
if (event == TRUE) {
ClearCommError(handler!, _errors, _status);
data = await _read(_readBytesSize);
if (data.isNotEmpty) {
yield data;
}
} else {
if (GetLastError() == ERROR_IO_PENDING) {
/// WaitForSingleObject is often timeout, so remove it
if (WaitForSingleObject(_over.ref.hEvent, 500) == 0) {
ClearCommError(handler!, _errors, _status);
print(_status.ref.cbInQue);
data = await _read(_status.ref.cbInQue);
if (data.isNotEmpty) {
yield data;
}
}
}
}
}
} I think I am going to be crazy because of the strange bug... |
@FengChendian If the idea was good enough and there is no other blocking factor besides this strange bug we could open up an issue on win32 package and get help to implement the |
@sabin26 Yeah. I will try to implement this feature if this bug fixed. And I will make OnDataReceivedFromSerialDevice and ReadOnListen are both optional in the future version. About this bug, I think maybe it is due to auto-reset event in dart. So I will wait for updating ResetEvent to test it. If it's not working, I will open an issue in win32. |
@FengChendian The issue that you raised on win32 package regarding the |
hi bro 馃憢馃槄
can you implement a stream for read data and a method onErr or onDone 馃檹
For example,馃 when the port is disconnected from the system, the error method is executed馃尮
The text was updated successfully, but these errors were encountered: