LaserWeb / LaserWeb4

Collaborative effort on the next version of LaserWeb / CNCWeb
GNU Affero General Public License v3.0
692 stars 186 forks source link

Problems on firefox with popup/setState #656

Closed xxxajk closed 1 month ago

xxxajk commented 1 year ago

First, I'm unfamiliar with js, so I'm very unsure where to even look..

Multiple pop-up windows showing queue state, and every one doesn't show progress... Here's the debug from the browser:

Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the Com component. warning.js:33 printWarning warning.js:33 warning warning.js:57 getInternalInstanceReadyForUpdate ReactUpdateQueue.js:46 enqueueSetState ReactUpdateQueue.js:207 setState ReactBaseClasses.js:62 handleConnectServer com.js:137 emit index.js:143 emitEvent socket.js:276 onevent socket.js:263 onpacket socket.js:228 emit index.js:143 ondecoded manager.js:200 emit index.js:143 add index.js:110 ondata manager.js:192 emit index.js:143 onPacket socket.js:323 emit index.js:143 onPacket transport.js:100 callback polling.js:83 onData polling.js:86 emit index.js:143 onData polling-xhr.js:188 onLoad polling-xhr.js:229 onreadystatechange polling-xhr.js:147 (Async: EventHandlerNonNull) create polling-xhr.js:143 Request polling-xhr.js:101 request polling-xhr.js:53 doPoll polling-xhr.js:78 poll polling.js:63 onData polling.js:93 emit index.js:143 onData polling-xhr.js:188 onLoad polling-xhr.js:229 onreadystatechange polling-xhr.js:147 (Async: EventHandlerNonNull) create polling-xhr.js:143 Request polling-xhr.js:101 request polling-xhr.js:53 doPoll polling-xhr.js:78 poll polling.js:63 onData polling.js:93 emit index.js:143 onData polling-xhr.js:188 onLoad polling-xhr.js:229 onreadystatechange polling-xhr.js:147 (Async: EventHandlerNonNull) create polling-xhr.js:143 Request polling-xhr.js:101 request polling-xhr.js:53 doPoll polling-xhr.js:78 poll polling.js:63 doOpen polling.js:23 open transport.js:44 open socket.js:159 Socket socket.js:100 open manager.js:108 timer manager.js:320 (Async: setTimeout handler) reconnect manager.js:313 onclose manager.js:293 emit index.js:143 onClose socket.js:526 setTransport socket.js:179 emit index.js:143 onClose transport.js:109 (Async: EventHandlerNonNull) addEventListeners websocket.js:75 doOpen websocket.js:61 open transport.js:44 probe socket.js:270 onOpen socket.js:290 onHandshake socket.js:344 onPacket socket.js:308 emit index.js:143 onPacket transport.js:100 callback polling.js:83 onData polling.js:86 emit index.js:143 onData polling-xhr.js:188 onLoad polling-xhr.js:229 onreadystatechange polling-xhr.js:147 (Async: EventHandlerNonNull) create polling-xhr.js:143 Request polling-xhr.js:101 request polling-xhr.js:53 doPoll polling-xhr.js:78 poll polling.js:63 doOpen polling.js:23 open transport.js:44 open socket.js:159 Socket socket.js:100 open manager.js:108 timer manager.js:320 (Async: setTimeout handler) reconnect manager.js:313 onclose manager.js:293 emit index.js:143

xxxajk commented 1 year ago

Additional thoughts: Perhaps forget about using a popup, and just display the queue/state in the control frame.

xxxajk commented 1 year ago

Screenshot_20230513_141821

cprezzi commented 1 year ago

What exactly did you install? The release version has no popups for progress. The only "progress" info is the "Queued: count" info on the Control tab (on top of the DRU). The queued count comes from the server part and only tells how many gcode lines are left in the queue (not jet sent to the controller). If you like to add a progress percentage, you can store the first count after starting the job and calculate the progress against the actual count.

xxxajk commented 1 year ago

Built from source on Linux. Perhaps it's the webpack-dev-server instead of the root url? I can program in no less than 10 different languages (probabbly more), and javascript unfortunatly isn't one of them. So where's the popup overlay coming from then? It eats a CPU core like candy, firefox reports it is basically stuck, and it keeps re-opening a new one upon closing it, or if it lost connection...

xxxajk commented 1 year ago

Latest test doesn't show that popup-- yet. I'm wondering if firefox had an old cached version of everything. if that's the case, perhaps the server end could send the header that instructs to not cache the page or elements? Once that message pops up, the browser ends up in trouble for sure.

xxxajk commented 1 year ago

Got it to happen, not sure how, but here's the result. Screenshot_20230515_232537

xxxajk commented 1 year ago

Tested chrome, same thing happens (the overlayed pop-up kind of thing), but chrome seems to handle the workload better.

xxxajk commented 1 year ago

Looking closer, and this is just a guess, from things I have seen before, but I did a little bit more digging, and it looks like the code is trying to buffer every status line it gets from the cutter. The program then places some of them into the scrolling buffer window. The buffered messages pile up, kind of like a memory leak (always allocates, never deallocates) and eventually the browser detects this as a serious problem (as it should) and disconnects the stream. I think you need to keep only a small amount of these, since all the data is logged anyway, say something like 5K lines?

The other part that should not be logged to that output scroller are the grbl status lines. E.G. S_XYZ:5. Yes, Marlin can output these for compatibility, and I have them turned on so I can visually monitor buffer levels. Normally other programs ignore the status, as they should. I'm guessing here that anything your parser does not understand it emits to the scroller, however these simply spam the display, scrolling off more important messages.

My current work-around is to save the gcode, then send it via pronterface, but this is less than ideal, since pronterface lacks some of the nice controls that are laser/CNC focused.

Other than that, the program works quite well, although there would be a few things that could be improved with the gcode output, which would make it do the work quicker.

harlock999 commented 1 year ago

I don't have much time these days to dig into this, but what is the OS you are using and nodejs and npm version? I mention this as the official frontend as currently available github is using older nodejs dependencies. @easytarget got something more modern going which I'm using almost on a weekly basis without issues. The popup you are getting looks like a nodejs dependency that got upgraded with new features that doesn't make it 100% compatible with the older code, like some console debug printing.

cprezzi commented 1 year ago

LaserWeb was mainly developped for and tested with grbl and smoothieware. For Marlin it stongly depends on the version. You should use a marlin version/configuration that can handle laser power (S) inside of g1-g3 commands and it should only report ok (not the whole gcode), plus it should send work coordinates in statusReports. The server only drops ok's, all other reports from the controller are forwarded to the frontend so it can handle it. The frontend posts all unknown messages to the "terminal" box (along with some specific status reports). This way the user can see messages that are not handled by the software.

But I don't think this is the cause for the popups. I guess the popups might come from different versions of nodejs or dependencies.

xxxajk commented 1 year ago

@harlock999 Sure...

Linux 64bit on AMD Ryzen 7 3700X 8-Core Processor Overclocked to ~4050MHz with 32GB DDR4
npm --version
6.14.4
nodejs --version
v10.19.0

@cprezzi Totally aware of that, the version I am using is grbl compatible, based on Marlin-2.1-bugfix. If you want to see everything as far as the code, it is available here: https://github.com/xxxajk/Marlin/tree/bugfix-2.1.x-anymcu G[123] Sx G0 defaults to laser off M3 and M4 turn laser on M3 Sx and M4 Sx Enables laser for the next move at the specified power x M3 I Sx and M4 I Sx I instructs Marlin's planner to adjust laser power x during a move in relationship to the current target speed I modified it so that the Sxparameter is optional here, If it is missing it defaults to 100% (equal to S255) if Sx is included parameter on G[123], Sx parameter value wins that was asked for The idea (for my software that I wrote) is that I can issue something like M5 I S 128 and I do not need to specify the S parameter for any laser on moves thereafter, which compacts the gcode M5 turns laser off, sets the remembered value to zero for safety even if I was not specified M5 I turns laser off, laser off for G[123] moves, and sets the remembered value to zero for safety

That all said, I'm quite familiar with gcode, how it works, and the different "flavors" and wrote my own gcode generator.

Now you are possibly wondering why on earth I would write yet another gcode generator for myself, or not. That's quite easy to answer if you are curious at all... Nothing outputs at the accuracy I require when dealing with RS-274X GERBER format, because I end up having to convert it to an image, which screws up the whole idea of using RS-274X in the first place. After conversion, everything I have tried stretches things totally out of wack, or the resolution is not high enough. I'm not talking about the laser's actual dot, I mean the ability to specify the step resolution, and use that in conjunction with the laser to generate highly accurate printed circuit boards directly in FreeCAD. And yes, eventually I do plan on releasing it once I manage to optimize the paths. What's nice is that I can take my generated gcode, bring it into lasewrweb4, and check that the offsets etc are OK. I could just as well use pronterface, slic3r, or cura, but they don't show the burn paths at all, because it is expecting a 3d printer with extruder specified in the G codes.

xxxajk commented 1 year ago

BTW, I "fixed" the problem, by making src/components/command-history.js write method do nothing. That's the entire issue, it adds children with no limits on how many. When spilling information at 1Mbit/sec, memory goes away pretty fast. What this method needs to do is check the size, and pop the head child off the list of children, which, I do not know how to do. If this was limited to something reasonable, it would not be an issue.

cprezzi commented 1 year ago

@xxxajk Your project sounds very interesting.

Did you see the folowing instructions? https://github.com/LaserWeb/lw.comm-server/wiki/Manual-Installation-(RasPi) There we use node 12 and some dependencies might need that base.

I allways suggest to first only install lw.comm-server and open the integrated frontend (on localhost:8000) from a web browser (prefeably Chrome). This way you can be sure that the backend and frontend are compatible. Reason: The latest lw4 frontend is not compatible with the last lw.comm-server release, because there are some unfinished changes in the socket protocol.

xxxajk commented 1 year ago

@cprezzi of course. However everything is fine without the display box... which doesn't matter on Linux anyway since I can tail -f logfile.txt and see anything important there... As stated before, no matter what you are using, no matter the browser, there remains the fact that there is no trimming of the scrolling output window, which, because it has no limits, chews up memory on the browser, and that causes two issues. First, the obvious one is that you will OOM eventually, and second, the browser, keeping track of everything, gets slower and slower in deciding what exactly to render. This slowdown gets so bad that it causes a disconnection, as evidenced in the profile screenshot and other screenshots above. This is simple, or should be simple to solve. you simply somehow, provide a limit of child nodes, poping the last one off the top before adding the new one if the limit is reached. How to do that exactly in js, I have not the skills or understanding. This fix alone will increase overall performance in any case. Another performance gain can be had by limiting the rate of rendering updates to that scrolling window. This is a common speedup used in basically every terminal output I have ever seen. Basically you don't update anything if there are too many updates within a window, say, something like every 0.02 seconds. So, you have basically a 50Hz timer that checks if there has been a change, then change it all at once, instead of on every change, and you only reset the timer after you either render or not. It's not a free-running timer, it's set after the check and after any rendering was done. To be clear on that, here would be the pseudo code for the rendering check:

render() {
        if (buffer_changed) {
                buffer_changed=false;
                render_scroll_window();
        }
        set_timeout(0.02);
}
cprezzi commented 1 year ago

You are correct about the "unlimited" terminal box, but this should never be a problem, because with a correctly configured (supported) firmware there should only be one message when starting the job and one at the end, but no messages while running the job. So the terminal box shoul never contain a lot of lines, even when running many jobs over a whole day. I suggest to rather focus on why you get so many messages and disablke them on the firmware or filter them on serverside.

By the way, the frontend is using react for rendering the gui, which only renders needed parts.

xxxajk commented 1 month ago

Wrote my own gcode generator, for my needs anyway, in FreeCAD/Python, meh... Faster, does a better job. ;-)