-
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -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; | ||||||||||
|
||||||||||
pub struct SerialPort { | ||||||||||
pub file: std::fs::File, | ||||||||||
} | ||||||||||
|
@@ -37,8 +39,17 @@ impl SerialPort { | |||||||||
unsafe { | ||||||||||
let mut timeouts: winbase::COMMTIMEOUTS = std::mem::zeroed(); | ||||||||||
check_bool(commapi::GetCommTimeouts(file.as_raw_handle(), &mut timeouts))?; | ||||||||||
timeouts.ReadIntervalTimeout = 10; | ||||||||||
check_bool(commapi::SetCommTimeouts(file.as_raw_handle(), &mut timeouts))?; | ||||||||||
// How to set the COMM timeouts so the behavior is a bit more sane | ||||||||||
// https://learn.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-commtimeouts#remarks | ||||||||||
// TODO: Expose these as configuration options on Windows | ||||||||||
timeouts.ReadIntervalTimeout = std::u32::MAX; | ||||||||||
timeouts.ReadTotalTimeoutMultiplier = std::u32::MAX; | ||||||||||
// Default timeout is 10ms | ||||||||||
timeouts.ReadTotalTimeoutConstant = DEFAULT_TIMEOUT; | ||||||||||
// Set write timeouts | ||||||||||
timeouts.WriteTotalTimeoutMultiplier = std::u32::MAX; | ||||||||||
timeouts.WriteTotalTimeoutConstant = DEFAULT_TIMEOUT; | ||||||||||
Comment on lines
+50
to
+51
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||||||||||
timeouts.check_bool(commapi::SetCommTimeouts(file.as_raw_handle(), &mut timeouts))?; | ||||||||||
} | ||||||||||
Ok(Self::from_file(file)) | ||||||||||
} | ||||||||||
|
@@ -66,8 +77,8 @@ impl SerialPort { | |||||||||
unsafe { | ||||||||||
let mut timeouts = std::mem::zeroed(); | ||||||||||
check_bool(commapi::GetCommTimeouts(self.file.as_raw_handle(), &mut timeouts))?; | ||||||||||
timeouts.ReadIntervalTimeout = 0; | ||||||||||
timeouts.ReadTotalTimeoutMultiplier = 0; | ||||||||||
timeouts.ReadIntervalTimeout = std::u32::MAX; | ||||||||||
timeouts.ReadTotalTimeoutMultiplier = std::u32::MAX; | ||||||||||
timeouts.ReadTotalTimeoutConstant = timeout.as_millis().try_into().unwrap_or(u32::MAX); | ||||||||||
check_bool(commapi::SetCommTimeouts(self.file.as_raw_handle(), &mut timeouts)) | ||||||||||
} | ||||||||||
|
@@ -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 commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
timeouts.WriteTotalTimeoutConstant = timeout.as_millis().try_into().unwrap_or(u32::MAX); | ||||||||||
check_bool(commapi::SetCommTimeouts(self.file.as_raw_handle(), &mut 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.
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.