-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
[WIP] Asynchronous serial #3209
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
Conversation
|
looks like travis still using my previous branch. error on line 3170 but in my repository this line is different https://github.com/linvinus/OctoPrint/blob/5b6bd5a7f53bf815a8eef0cc1b73ee0dbd7d189b/src/octoprint/util/comm.py#L3170 |
|
@foosel now working good with maintenance branch |
|
@foosel hi, i have made a little improvement - clear send queue on canceling. |
foosel
left a comment
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 found some things that need clarification and some things that need changing. Could you take a look?
src/octoprint/util/comm.py
Outdated
| self._blockWhileDwelling = settings().getBoolean(["serial", "blockWhileDwelling"]) | ||
| self._send_m112_on_error = settings().getBoolean(["serial", "sendM112OnError"]) | ||
| self._currentLine = 1 | ||
| self._AdvancedOkSupported = False |
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.
New variables should be in snake_case - all lower case, words spaced with an underscore.
I know that some of the existing variables in the code are still camelCase, but they are slowly being migrated as well.
Please never start a variable name with an upper case letter, that makes it look like a class.
It would also help to know what these variables mean, after having read through all the other changes I'm still unclear on it.
Finally, whether this feature is actually supported or not should receive an additional feature flag (similar to busy support). Feature flag should be enabled by default, but _AdvancedOkSupported should only ever become True if support is detected and said flag is True too. That will allow to disable it and fall back on pure ping pong in case of broken firmwares and/or very specific bugs in this implementation.
src/octoprint/util/comm.py
Outdated
| # now we just wait for the next clear and then start again | ||
| self._clear_to_send.wait() | ||
| if ( self._AdvancedOkSupported ): | ||
| if ( self._state == self.STATE_PRINTING |
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.
What about STATE_PAUSING, STATE_RESUMING, STATE_CANCELLING? Printing state is more than just STATE_PRINTING.
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 code filling the send queue with respect to marlin buffers, in non printing state older synchronous mode should be working fine
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 aforementioned states are also printing states however.
src/octoprint/util/comm.py
Outdated
| or self._AdvancedOkSendNextLines < 0 | ||
| ): | ||
| # now we just wait for the next clear and then start again | ||
| while True: |
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 feels very dangerous and is a huge deadlock waiting to happen. It shouldn't happen as long as sensible data is being exchanged, but that's not something we can count on with finicky cabling and controllers. This needs some kind of fallback to ensure we don't get stuck in this loop forever.
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.
you are right, self._advanced_ok_last_line must be retested to -1 at communication timeout, if we loose synchronization
src/octoprint/util/comm.py
Outdated
| if (self._LastProcessedLine >= self._SynchronousCommand): | ||
| break | ||
| else: | ||
| cruLineTimeout=200 * self._serial.timeout #wait untill busy message will be recieved |
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.
What is cruLineTimeout supposed to mean? Typo of cur? Write out words fully please, for readability, and in snake_case => current_line_timeout.
src/octoprint/util/comm.py
Outdated
| break | ||
| else: | ||
| cruLineTimeout=200 * self._serial.timeout #wait untill busy message will be recieved | ||
| while( self._currentLine > self._AdvancedOkSendNextLines and cruLineTimeout >0): |
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 completely unclear on what this whole block does. Needs more comments ;)
And busy polls are always a red flag - do we really need that here? Comments might clear up why, if so.
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.
sorry haven't enough time now, simply in asynchronous transaction mode, synchronization through _clear_to_send queue is not working at all.
here we simply waiting until marlin free buffer, or timeout will occurred.
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.
Keep in mind that I will have to maintain this code going forward - this needs proper documentation so that that will be possible.
src/octoprint/util/comm.py
Outdated
| Args: | ||
| line (str): the line to parse | ||
| Returns: | ||
| last_processed_line |
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.
From the looks of it this should rather be
def parse_advanced_ok_line(line):
"""
Parses the provided ADVANCED_OK line.
Format:
ok N<last processed line> P<free planner slots> B<free command slots>
Args:
line (str): the line to parse
Returns:
(dict or None) A dict containing the last processed line number (``N``), the amount of free
slots in the motion planner buffer (``P``) and the amount of free slots in the command buffer (``B``),
or None if no match could be made.
"""
# ...And something about the whitespace seems to be fishy in the function as well.
That sounds like it should be a separate PR. Let's concentrate on what we have here first. |
…, _pool_for_freebuffer, more asynchronous commands are added, improve regexp for advanced_ok, more comments
|
Hello ! |
|
Hi guys. Until the 5th of August I'm on vacation. |
|
|
@Evg33 travis don't see any conflicts |
|
@Evg33 Ohh, in mobile version i don't see that. |
|
@Evg33 resolved |
|
Currently I have noticed the following:
For speed test i use the following model https://www.thingiverse.com/thing:1185928 Model was sliced in Cura with following settings Marlin have SLOWDOWN option to resolve that cases but it will slow down printing and anyway printing quality will be poor.
|
|
@linvinus Thank you !!! |
|
@Evg33, no problem. let me know if you have any questions. It seems that you are the first man who will testing this patches, i will appreciate for any feedback! |
fix merge of maintenance branch
|
@linvinus I have to admit that I lost track of this somewhat after my initial review... I see that you pushed some changes in the meantime, but some points appear to remain unaddressed. Additionally from your comment here it sounds like this isn't ready for prime time yet due to firmware issues, or did I misunderstand that? |
|
@foosel hi! Currently i'm planning to replace 8-bit processor with STM32F1 and then compare print quality. |
|
+1 for this... printing over wifi at 300mm/s on a tinyfab cpu (LPC1769) in a UP BOX with the linvinus version, with the oficial version, I have multiple slowdowns and blobs, and even comunications errors... |
foosel
left a comment
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.
There's still a whole bunch of open points.
| "additionalPorts": [], | ||
| "additionalBaudrates": [], | ||
| "longRunningCommands": ["G4", "G28", "G29", "G30", "G32", "M400", "M226", "M600"], | ||
| "bufferedCommands": ["G0", "G1", "G2", "G3", "G10", "G11", "G20", "G21", "G42", "G53", "G54", "G55", "G56", "G57", "G58", "G59", "G90", "G91", "G92", "M3", "M4", "M5", "M104", "M106", "M107", "M117", "M140"], |
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 list of buffered commands still seems way too long. M104 is definitely not buffered. It might be that some of the other ones are buffered on your specific fork, but not in general. We should really stick to universally buffered commands here which - again - are G0 through G3 and G28 through G32. Anything more than that and we risk issues with firmware variants.
| needs_further_handling = "T:" in line or "T0:" in line or "B:" in line or "C:" in line or \ | ||
| "X:" in line or "NAME:" in line | ||
| handled = (line == "wait" or line == "ok" or not needs_further_handling) | ||
| handled = (line == "wait" or line == "ok" or (line.startswith("ok") and self._state == self.STATE_PRINTING) or not needs_further_handling) |
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.
starting, pausing, cancelling, resuming, finishing? Probably better: self._state in self.PRINTING_STATES
| free_planner_buff = parsed.get("P") | ||
| free_command_buff = parsed.get("B") | ||
|
|
||
| #if (self._advanced_ok_last_line >= self._advanced_ok_wait_for_line ): |
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.
Please remove unused code
| #if (self._advanced_ok_last_line >= self._advanced_ok_wait_for_line ): | ||
| self._advanced_ok_max_line = self._advanced_ok_last_line + free_command_buff#max(free_planner_buff,free_command_buff) | ||
| else: | ||
| #if(not ("T:" in line) ): #todo, report marlin bug M105 Report Temperatures without line number when advanced_ok enabled |
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.
Please remove unused code
|
|
||
| if ( self._advanced_ok_detected and self._state == self.STATE_PRINTING): | ||
| with self._line_mutex: | ||
| linenumber = self._current_line - 1 |
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.
You'll have to take into account that a line might have been sent without a number (possible for in-band commands that aren't GCODE, or just for certain configuration options).
|
|
||
| if( self._advanced_ok_max_line > 0 and not self._send_queue.resend_active): | ||
| if( gcode in self._advanced_ok_buffered_cmds ): | ||
| if(linenumber < self._advanced_ok_max_line and self._continue_sending()): |
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.
needs documentation
| self._send_m112_on_error = settings().getBoolean(["serial", "sendM112OnError"]) | ||
| self._current_line = 1 | ||
| self._advanced_ok_detected = False | ||
| self._advanced_ok_max_line = -1 #the last line which we could send in asynchronous mode without cause overflow, negative value used to interrupt any waitings |
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.
rename: self._advanced_ok_last_sent_line
| self._current_line = 1 | ||
| self._advanced_ok_detected = False | ||
| self._advanced_ok_max_line = -1 #the last line which we could send in asynchronous mode without cause overflow, negative value used to interrupt any waitings | ||
| self._advanced_ok_last_line = -1 #the last line that printer has successfully processed |
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.
rename: self._advanced_ok_last_ackd_line
| self._advanced_ok_max_line = -1 #the last line which we could send in asynchronous mode without cause overflow, negative value used to interrupt any waitings | ||
| self._advanced_ok_last_line = -1 #the last line that printer has successfully processed | ||
| self._advanced_ok_wait_for_line = -1 #the next line which we will waiting before continue sending | ||
| self._advanced_ok_buffered_cmds = settings().get(["serial", "bufferedCommands"]) |
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.
rename: self._advanced_ok_buffered_commands
|
@foosel hi! Please have a look at figure 1. Others, instead, goes to special buffer with size of BLOCK_BUFFER_SIZE, and OK report sends immediately after checking crc summ, but before actual physical actions was performed. The problem is "poor printing quality like bulbs or unstable line width". Where there come from? Because of STL file is triangle based, it unable to describe circles. So to resolve this printing problems we should
You may notice that all of them is actually hardware related, so simple use powerful processor with big enough ram and you will not have this problems. If we can't change hardware, what can be done from software perspective? From software we may optimize protocol between octoprint and firmware. Even the shortest loop (T1 -> T2 -> T3 -> T4 -> T5) actually too long, this is meas that So, instead of waiting for report from T5 step, we may send new commands in advance. THIS IS WHAT I MEAN BY "ASYNCHRONOUS COMMANDS" How much? As much as enough space in RX_BUFFER. Because RX_BUFFER is flat and commands usually smaller than 96bytes, if we will be smart enough, on octoprint side we may calculate free space in RX_BUFFER and send commands with respect of that. So, doesn't matter is the command is "buffered" or not in terms of firmware, we send it in advance. But not all of commands may be send in advance, that is why we have bufferedCommands list, it contain commands that may be send asynchronously without causing error or unpredictable behavior in firmware. |
|
I'm well aware of the buffering situation in Marlin and similar firmwares (and in fact it's three buffers: rx -> command -> planner) and even had to explain it to current Marlin maintainers ;) I'm also aware of what this patch is trying to achieve and how it's trying to achieve it. I've been playing with stuff like this long before the Remember that I have to wrap my head around code I didn't write myself here, and if you call something "buffered" commands, then I assume you mean those that are officially considered buffered, as in, they go into the planner buffer. Not arbitrary commands. After taking another look at the code I now understood that you are actually concentrating on keeping the command buffer filled, and only that, not the planner buffer.
This is where you've lost me. Things should go straight from RX to Command buffer, in the same order as received, without any changes other than stripping line number and checksum, if set, and potentially requesting a resend first. Nothing should be executed here and hence no command should be blacklisted from getting sent to fill the command buffer. If we are tracking the command buffer here and not the planner buffer, then we shouldn't need a distinction between command types because all commands are the same until it comes to the question, "planner buffer yes or no". And the command buffer will always be executed in order. RX to command buffer in Marlin is being done here. No So either I'm missing something crucial here, or this whole buffered list that seems to be main concern here isn't even needed (and misnamed since the term "buffered command" is already reserved for commands that go into the planner buffer). And if I'm missing something crucial here, please tell me what. |
Absolutely right! Initially i also playing with planner buffer, trying to make it filled full all the time as much as possible, but as i say on 8-bit processor bottleneck is processor performance. NOTE 1
There is no big differences between this strategies when processor performance is not enough. NOTE 2: Because "Command Buffer" - is used only for resend purpose, it useless for us, we may reduce it up to one (BUFSIZE = 1) and instead increase RX_BUFFER as much as possible. NOTE 3 Now "B" is constant, but "P" is very unstable and non of them is related to free bytes in RX_BUFFER. So information about buffers in advanced_ok is completely useless. Only line number is good to know. But then how to send commands in advance with respect to free space in RX_BUFFER? (to prevent RX orverflow)
here RX_BUFFER and MAX_CMD_SIZE is firmware constants which,in general, is unknown to octoprint :( I think we can make additional option for user where he can setup value (RX_BUFFER/MAX_CMD_SIZE - 1) suitable for his firmware. Now you know everything that is important to know about this patch.
you are almost right, the reason why i use bufferedCommands list is that we can't send some commands in advance because otherwise we will loose synchronization this function is used for fast commands https://github.com/linvinus/OctoPrint/blob/3a5bdb78d093f2d3cf322420a3bcee5181522d61/src/octoprint/util/comm.py#L3187 while this one for slow https://github.com/linvinus/OctoPrint/blob/3a5bdb78d093f2d3cf322420a3bcee5181522d61/src/octoprint/util/comm.py#L3202 |
|
This pull request has been mentioned on OctoPrint Community Forum. There might be relevant details there: https://community.octoprint.org/t/the-ongoing-usb-conspiracy-theory/6341/85 |
|
if you wanna test with something that will be octoprint bottleneck, take a look at klipper. we have to use virtual sd to get speed on circles (70mm/s+). |
|
Connecting to original #450 issue... |
|
Closing this as it doesn't look like this will work reliably with the current comm layer and/or firmware implementations, and the review comment were never addressed either. Additionally there are also now projects like https://github.com/chendo/BufferBuddy |




to a large audience (ideally all users of OctoPrint)
made sure your changes don't interfere with current development by
talking it through with the maintainers, e.g. through a
Brainstorming ticket
new feature, or maintenance if it's a bug fix or improvement of
existing functionality for the current stable version (no PRs
against master or anything else please)
(no PRs from your version of master, maintenance or devel please),
e.g. dev/my_new_feature or fix/my_bugfix
no dead code, ideally only one commit - rebase and squash your PR
if necessary!
.less source files, not the .css files (those are generated with
lessc)
have added unit tests
nothing broke
What does this PR do and why is it necessary?
Resolve #2834 #2834
How was it tested? How can it be tested by the reviewer?
compile marlin with following options
print some cylinder at maximum printing speed, compare print quality
Any background context you want to provide?
no
What are the relevant tickets if any?
#2834