-
Notifications
You must be signed in to change notification settings - Fork 12
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’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tweak windows COMMTIMEOUTS settings #13
Tweak windows COMMTIMEOUTS settings #13
Conversation
the normal developer experience which is mostly POSIX-style these days See ticket de-vri-es#7 for discussion
Basically, if there is data to read, it returns immediately. If there is no data, it waits until the configured timeout has passed. If there is data to write, it writes immediately. If the timeout passes when writing, it returns immediately. This matches the expected behavior which for many developers is POSIX style. The more advanced settings could be exposed as additional config items in the windows config. |
Please note, I have no way of testing this. I am just applying the changes I've seen in other serial port packages to this one. |
Thanks! On a first glance and from the docs, it looks good. I'll do some tests on a Windows machine too. I just need to get my hands one one first :p |
@@ -8,6 +8,8 @@ use winapi::shared::minwindef::{BOOL, HKEY}; | |||
use winapi::shared::winerror; | |||
use winapi::um::{commapi, fileapi, handleapi, ioapiset, minwinbase, synchapi, winbase, winnt, winreg}; | |||
|
|||
const DEFAULT_TIMEOUT: u32 = 10; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a default timeout, I think 10ms is too short. Normally read/writes can block for much longer before timing out. Maybe even a few seconds would be good.
The value of 10 ms was the timeout between receiving character data, where it could be sensible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This time out, at least in POSIX land is how we wait for data to be available, or block on the serial port waiting to write some data. Usually its basically 0 for serial ports. Usually you try and read/write data immediately without a timeout, and the system tells you how many bytes you actually read/wrote.
I know windows has its own weird windows-isms, so I don't know.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Theoretically I guess windows might provide support for filling a larger buffer within a timeout window timeframe, but the purpose of this PR was to try and approximate the simpler POSIX style serial port handling, and we could then expose a windows-only api.
Again, should dig around in the PySerial sources for windows. Hmm
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a lot of people doing serial on windows get confused because many other packages on windows ( pyserial, arduino ) basically configure the windows serial port handling to be more like POSIX.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm also in favor of emulating POSIX behaviour, but I still think 10ms is very short. On Linux, the default timeout for a virtual terminal seems to be infinite (or at least longer than my patience).
Note that the timeout only triggers if no data is available. If data is available, it will still return without waiting for the timeout. That's why I think 10ms is too short as a default timeout.
timeouts.WriteTotalTimeoutMultiplier = std::u32::MAX; | ||
timeouts.WriteTotalTimeoutConstant = DEFAULT_TIMEOUT; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
timeouts.WriteTotalTimeoutMultiplier = std::u32::MAX; | |
timeouts.WriteTotalTimeoutConstant = DEFAULT_TIMEOUT; | |
timeouts.WriteTotalTimeoutMultiplier = 0; | |
timeouts.WriteTotalTimeoutConstant = DEFAULT_TIMEOUT; |
DWORDMAX
is not a special value for the write timeouts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This from PySerial win32 seems to show some other behavior
# Set Windows timeout values
# timeouts is a tuple with the following items:
# (ReadIntervalTimeout,ReadTotalTimeoutMultiplier,
# ReadTotalTimeoutConstant,WriteTotalTimeoutMultiplier,
# WriteTotalTimeoutConstant)
timeouts = win32.COMMTIMEOUTS()
if self._timeout is None:
pass # default of all zeros is OK
elif self._timeout == 0:
timeouts.ReadIntervalTimeout = win32.MAXDWORD
else:
timeouts.ReadTotalTimeoutConstant = max(int(self._timeout * 1000), 1)
if self._timeout != 0 and self._inter_byte_timeout is not None:
timeouts.ReadIntervalTimeout = max(int(self._inter_byte_timeout * 1000), 1)
if self._write_timeout is None:
pass
elif self._write_timeout == 0:
timeouts.WriteTotalTimeoutConstant = win32.MAXDWORD
else:
timeouts.WriteTotalTimeoutConstant = max(int(self._write_timeout * 1000), 1)
win32.SetCommTimeouts(self._port_handle, ctypes.byref(timeouts))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
elif self._write_timeout == 0:
timeouts.WriteTotalTimeoutConstant = win32.MAXDWORD
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thats an attempt to make the timeout as long as possible. It's not a special value.
Also, I'm suggesting to change the WriteTotalTimeoutInterval
to 0, not WriteTotalTimeoutConstant
.
@@ -85,7 +96,7 @@ impl SerialPort { | |||
unsafe { | |||
let mut timeouts = std::mem::zeroed(); | |||
check_bool(commapi::GetCommTimeouts(self.file.as_raw_handle(), &mut timeouts))?; | |||
timeouts.WriteTotalTimeoutMultiplier = 0; | |||
timeouts.WriteTotalTimeoutMultiplier = std::u32::MAX; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
timeouts.WriteTotalTimeoutMultiplier = std::u32::MAX; | |
timeouts.WriteTotalTimeoutMultiplier = 0; |
Yeah, there is about zero info on the write side on what the write timeout should be. |
Adding this for notes Will dig in here and see how they do it since a lot of windows users coming from python to rust are getting tripped up and it seems PySerial sets the timeout behavior to be more posix style. |
You may already be familiar with this, but the key info on the Windows DCB timeout settings and associated behavior is found at link https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddser/ns-ntddser-_serial_timeouts In my experience as an IoT/instrument software developer, the following excerpt is what I use in practice most often => If both ReadIntervalTimeout and ReadTotalTimeoutMultiplier are set to MAXULONG, and ReadTotalTimeoutConstant is set to a value greater than zero and less than MAXULONG, a read request behaves as follows:
|
Closing this in favor of #15: that one sets a 3 second default timeout and it sets It also sets the same timeout by default for the Unix implementation. Thanks for all the discussion and suggestions! |
Matches the normal developer expectations around POSIX style timeouts
See ticket #7 for discussion