Skip to content
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

IOPort/Windows: Fix wrong break condition handling #262

Merged
merged 4 commits into from
Dec 1, 2023

Conversation

qx1147
Copy link
Contributor

@qx1147 qx1147 commented Nov 30, 2023

Ignore break conditions completely (like BreakBehaviour=Ignore), rather than treating them as errors without further handling them in a useful way. As discussed in Pull request #821, this commit will change IOPort's behaviour in a somewhat incompatible way, mostly because the user script won't have any possibility anymore to find out about break conditions.
According to the help text of IOPort('OpenSerialPort',...) for the BreakBehaviour setting, the setting is ignored for Windows. But it was not further specified what kind of break behaviour was actually implemented. The implementation so far treated a break condition as an error, which caused data to be discarded when the condition was detected during a read operation. Moreover, the user script was notified about the error through a return parameter of the IOPort's read or write function. Beyond that, no further actions were taken like, for example, preserving data that was received before the break condition and re-starting normal data reception at the end of the break condition. However, implementing such handling would be very difficult if not impossible without low-level OS support. So, ignoring the break condition altogether seems to be the better solution and is consistent with the BreakBehaviour=Ignore setting, which is the default for other OSs as well.

Ignore break conditions completely (like BreakBehaviour=Ignore), rather than treating them as errors without further handling them in a useful way.
if (estatus > 0 && inerrmsg) {
if (estatus & CE_BREAK) { sprintf(errmsg, "IOPort: Break condition on receive line detected.\n"); strcat(inerrmsg, errmsg); }
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can leave this line for future use.

@@ -369,7 +369,8 @@ PsychError IOPORTOpenSerialPort(void)
"or on OS/X and Linux in 'Cooked' processing mode as line delimiter. A setting of -1 will try to disable the line terminator.\n\n"
"DTR=os default -- Setting for 'Data Terminal Ready' pin: 0 or 1.\n\n"
"RTS=os default -- Setting for 'Request To Send' pin: 0 or 1.\n\n"
"BreakBehaviour=Ignore -- Behaviour if a 'Break Condition' is detected on the line: Ignore, Flush, Zero. On Windows, this setting is ignored.\n\n"
"BreakBehaviour=Ignore -- Behaviour if a 'Break Condition' is detected on the line: Ignore, Flush, Zero. On Windows, this "
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, thinking further about this: I think we should not "Ignore" the setting, instead we should validate the parameter and error abort with a "Setting not supported on Windows" error message or such, if a setting other than Ignore is specified.

This way the default or explicit setting of Ignore will do the right thing - and compatible to other OS'es, but users get a heads up if they try to ask for a behaviour that is fine on Linux/macOS, but unsupported on Windows. I think that would be the most sane approach, and the one I should have implemented in the first place.

PsychSerialWindowsGlue.c already has the commented out if-else statements, so one just has to uncomment and update it accordingly, and change the last sentence of the help text here to "On Windows, only 'Ignore' is allowed."

@kleinerm
Copy link
Owner

So some more suggestions for a cleaner solution, the one that I should have done in the first place. You can make them and force-push to update this pull request, or I can do them, up to you.

I'll probably do another PTB bugfix release very soon, probably already tomorrow or at the weekend. If you get to it by early noon tomorrow, great. Otherwise I may do it.

@qx1147
Copy link
Contributor Author

qx1147 commented Dec 1, 2023

Okay. I hope the 2nd commit is what you had in mind.
The unusual way of how I check for the Ignore value points at a general flaw in the configuration string handling. This is a different topic, but most user errors will currently go undetected if the default setting string contains a setting. Moreover, the default settings might take precedence no matter what, unless I am missing something. Check what happens to a 'StopBits=2' user setting. I thought about fixing it, but it is not so simple without breaking compatibility.

Fixup help string formatting.
Fixup for consistency with Unix error message format.
@kleinerm kleinerm merged commit be5b75d into kleinerm:master Dec 1, 2023
1 check passed
kleinerm added a commit that referenced this pull request Dec 1, 2023
…r settings.

As GitHub user @qx1147 found out during a code review, there is a flaw in
the handling of serial port options, where default serial port options may
override user provided configuration options, so the wrong settings are
silently applied! This only happens if the user script provides different
settings in IOPort('OpenSerialPort', ..., settings), ie. on initial port
open. Calls to IOPort('ConfigureSerialPort', ..., settings) are not
affected.

Patch this up by reordering the if-else ladders for Unix and Windows:
Default settings must come as the last else-if-else block before the
error handling for invalid parameters. This way, user provided non-default
settings for serial port parameters take precedence over the built in
defaults, as intended.

Luckily (dumb luck!), only a few options were mishandled, and most of these
are rarely changed by user scripts, specifically:

ProcessingMode=Cooked would get ignored, and raw mode used instead. Rarely
used on Linux/macOS and not supported on Windows anyways. Low impact.

BreakBehaviour=Ignore would be used instead of Flush or Zero. Not a
problem on Windows, as that is the only supported option. The fact that
this went unnoticed on Linux/macOS suggests not much use of Flush or Zero
by users scripts. No use by PTB itself. Low expected impact. Note that the
implementation of 'Flush' on the Unixes is of questionable use, as Flush
would not only flush read/write buffers, but also send a SIGINT, which may
end in unexpected ways on Unix, as we don't handle SIGINT specifically.

StopBits=2 would get ignored and the default of 1 stop bits would be used.
Unclear how many devices want 2 stop bits, but I haven't ever seen one, so
probably low expected impact.

DataBits=16 would get ignored on MS-Windows only and replaced by 8 bits,
whereas other settings are fine, and 16 bits is unsuported on Linux/macOS,
so probably rarely if ever used. Low expected impact.

FlowControl=None will be used instead of hardware or software flow control.
This can be a real bummer with potential real impact, as some devices do
support or recommend active flow control! In the case of PTB, the
CedrusResponseBox() driver for Cedrus response box devices would have
liked that setting.


Audit of Psychtoolbox internal user of IOPort, and of the demos, only
shows one bad case where things went sideways: CedrusResponseBox.m
This driver requested hardware flow control, but silently got instead NO
flow control! This is interesting, because during my testing I found
communication with Cedrus boxes always rather unreliable, and now I have
to wonder if this was because of the accidental lack of hardware flow
control? I can't find out, as I lost access to Cedrus devices long ago.

This whole parameter handling is somewhat fragile, and could do with an
improved implementation, but due to the severe lack of financial funding
for PTB, this is not an option in the foreseeable future. Not even code
review or testing of 3rd party contributions, if there were any. Luckily
this part of the driver is mostly static since years, so I guess we can
drag our feet longer and sit it out.

Another problem pointed out by @qx1147 is indeed that wrong options, e.g.,
due to typos in users experiment scripts, would not lead to an error abort
or warning, but would get silently ignored in many cases. Not great at all,
but lack of funding will leave this problems also unsolved in the near- to
midterm, possibly forever. Life sucks...

See also PR #262 for discussion. Thanks to @qx1147 for catching this!

Work time spent on IOPort improvements: 5 hours, resulting in loss of
at least 1000 Euros of billable time to the project!
@qx1147
Copy link
Contributor Author

qx1147 commented Dec 4, 2023

Yeah, probably not the worst idea to only address what would be considered a bug. Unfortunately, the more robust and user-friendly we make the configuration string processing (e.g., by making it case-insensitive, ignoring white spaces, and catching unprocessed, i.e., misspelled parameters), the higher is the risk of breaking old user scripts by then silently obeying potentially wrong configuration settings which have been ignored before ("wrong" in the sense of making the serial communication not work anymore). Therefore, "Stick to the shit!" might even be the most sensible choice here.
Maybe a warning in the help text for 'OpenSerialPort' and 'ConfigureSerialPort' would be helpful, at a prominent place, that the configuration string is absolutely case-sensitive and does not tolerate white-spaces around the "=" character, and that consistency checking is weak.

@kleinerm
Copy link
Owner

kleinerm commented Dec 17, 2023 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants