-
-
Notifications
You must be signed in to change notification settings - Fork 19.3k
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
MarlinSerial emergency-command parser (with M108) #4226
MarlinSerial emergency-command parser (with M108) #4226
Conversation
5ccb52e
to
929e36a
Compare
@AnHardt Looking at the emergency parser, it seems like in some cases, it might be able to go into I see that it doesn't account for the For example, when in
|
a81efce
to
7c6433c
Compare
|
||
case state_N: | ||
if (!NUMERIC_SIGNED(c)) state = state_RESET; | ||
break; |
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 can't see what the state_N is for.
Just ignore everything else than a 'M' in state_RESET and stay in state_RESET.
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.
The main thing I want to ensure is that we are at the start of the command and not anyplace else. If (for some reason) we get a command like "G12 M108" we want to ignore the "M108" in this case. So in state_RESET
(at the beginning of a new line) if we encounter "N" that's okay. We can skip past it and then go back to state_RESET
. If there's a space just skip. If we see "M", then we go to state_M
. With the added screening for "N", every other character can jump to state_IGNORE
and stay there for the rest of the line.
Hosts will most likely not send these commands with N
line numbers, but it's something to watch out for, to ensure we know we're still at the start of the command.
One thing that will fail here is something like "N123M108" without spaces. Unless…
if (!NUMERIC_SIGNED(c)) state = (c == 'M') ? state_M : state_RESET;
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.
Crap. Now i see.
No i don't. All dangerous commands, followed by text, begin with 'M'
7c6433c
to
7d0e93a
Compare
Another tweak could just wait until a CR before taking any action: switch (state) {
case state_RESET:
switch (c) {
case ' ': break;
case 'N': state = state_N; break;
case 'M': state = state_M; break;
default: state = state_IGNORE;
}
break;
case state_N:
switch (c) {
case '0': case '1': case '2':
case '3': case '4': case '5':
case '6': case '7': case '8':
case '9': case '-': case ' ': break;
case 'M': state = state_M; break;
default: state = state_IGNORE;
}
break;
case state_M:
switch (c) {
case ' ': break;
case '1': state = state_M1; break;
case '4': state = state_M4; break;
default: state = state_IGNORE;
}
break;
case state_M1:
switch (c) {
case '0': state = state_M10; break;
case '1': state = state_M11; break;
default: state = state_IGNORE;
}
break;
case state_M4:
state = (c == '1') ? state_M41 : state_IGNORE;
break;
case state_M10:
state = (c == '8') ? state_M108 : state_IGNORE;
break;
case state_M11:
state = (c == '2') ? state_M112 : state_IGNORE;
break;
case state_M41:
state = (c == '0') ? state_M410 : state_IGNORE;
break;
case state_IGNORE:
if (c == '\n') state = state_RESET;
break;
default:
if (c == '\n') {
switch (state) {
case state_M108:
wait_for_heatup = false;
break;
case state_M112:
kill(PSTR(MSG_KILLED));
break;
case state_M410:
quickstop_stepper();
break;
}
state = state_RESET;
}
} |
case 'M': state = state_M; break; | ||
default: state = state_IGNORE; | ||
} | ||
break; |
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.
How about:
case state_RESET:
switch (c) {
case 'M': state = state_M; break;
case 'G':
case 'T': state = state_IGNORE; break;
default: state = state_RESET;
}
break;
to omit state_N
EDITED 2 times
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.
Why only ignore G
and T
? What about a command (as artificial as this may be) like "Hey there M108!
". Although it doesn't start with G
or T
it would still proceed to state_M108
. We know N
and
are ok beforeM
. While everything else can go tostate_IGNORE
. This should result in our being instate_IGNORE
a lot more.
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 attempted to say: "Who sends crap - gets crap."
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.
7d0e93a
to
5a98a34
Compare
case state_N: | ||
if (!NUMERIC_SIGNED(c) && c != ' ') | ||
state = (c == 'M') ? state_M : state_RESET; | ||
break; |
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 quite unlucky about state_N
. Now we have 6 decisions and a assignment. In my original version there have been always 2 decisions and one assignment. The beauty got lost.
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.
case state_N:
switch (c) {
case ' ':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-': break;
case 'M': state = state_M; break;
default: state_RESET;
}
?
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.
A switch-case will actually generate several "decisions." A non-numeric comparison like if (c > '9' || c < '0')
will fail (most of the time) on the first comparison, resulting in only a single test. But let me compare the ASM output…
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.
Sure enough, the switch()
approach does generate smarter test ordering than the current logic.
case state_N:
switch (c) {
case '0': case '1': case '2':
case '3': case '4': case '5':
case '6': case '7': case '8':
case '9': case '-': case ' ': break;
case 'M': state = state_M; break;
default: state = state_IGNORE;
}
break;
case state_N:
switch (c) {
1ad8: 80 33 cpi r24, 0x30 ; 48
1ada: 08 f0 brcs .+2 ; 0x1ade <_Z16emergency_parserh+0x4c>
1adc: 50 c0 rjmp .+160 ; 0x1b7e <_Z16emergency_parserh+0xec>
1ade: 80 32 cpi r24, 0x20 ; 32
1ae0: 09 f4 brne .+2 ; 0x1ae4 <_Z16emergency_parserh+0x52>
1ae2: 4d c0 rjmp .+154 ; 0x1b7e <_Z16emergency_parserh+0xec>
1ae4: 8d 32 cpi r24, 0x2D ; 45
1ae6: 61 f5 brne .+88 ; 0x1b40 <_Z16emergency_parserh+0xae>
1ae8: 08 95 ret
default: state = state_IGNORE;
}
break;
5a98a34
to
a0e37b5
Compare
@AnHardt Should we disable |
I'd suggest to disable for now, to give us some more time to make some more tests with the freshly changed code. If we are golden i have no doubts it can be activated by default. |
cae2ba7
to
dd05919
Compare
Add an emergency-command parser to MarlinSerial's RX interrupt. The parser tries to find and execute M108,M112,M410 before the commands disappear in the RX-buffer. To avoid false positives for M117, comments and commands followed by filenames (M23, M28, M30, M32, M33) are filtered. This enables Marlin to receive and react on the Emergency command at all times - regardless of whether the buffers are full or not. It remains to convince hosts to send the commands. To inform the hosts about the new feature a new entry in the M115-report was made. "`EMERGENCY_CODES:M112,M108,M410;`". The parser is fast. It only ever needs two switch decisions and one assignment of the new state for every character. One problem remains. If the host has sent an incomplete line before sending an emergency command the emergency command could be omitted when the parser is in `state_IGNORE`. In that case the host should send "\ncommand\n" Also introduces M108 to break the waiting for the heaters in M109, M190 and M303. Rename `cancel_heatup` to `wait_for_heatup` to better see the purpose.
dd05919
to
2ee4e4f
Compare
The time impact of the parser is as low as possible to do its job. On the other side, compared to the time we waste while busy waiting for sending, this is nothing. |
Sure, as long as it remains truly compatible with the hardware, it would be good! |
Add E3D Revo variant
Add a emergency-command parser in MarlinSerial located in MarlinSerial's RX interrupt.
The parser tries to find and execute M112,M108,M410 before the commands disappear in the RX-buffer.
To avoid false positives M117, comments and commands followed by filenames (M23, M28, M30, M32, M33) are recognized.This enables Marlin to receive and respond to the Emergency command at all times - regardless of whether command buffers are full or not. It remains to convince host software to send the commands. To inform hosts about the new feature a new entry in the M115-report was made. "
EMERGENCY_CODES:M112,M108,M410;
".The parser is fast. It only needs two switch decisions and one assignment of the new state for every character.
One problem remains. If the host has sent an incomplete line before sending an emergency command the emergency command could be omitted when the parser is in
state_IGNORE
. In that case the host should send "\ncommand\n"Also introducing M108 to break the waiting for the heaters in M109, M190 and M303.
Rename
cancel_heatup
towait_for_heatup
to better see the purpose.