Sienci-Labs / gsender

Connect to and control Grbl-based CNCs with ease
https://sienci.com/gsender/
Other
185 stars 45 forks source link

Raspberry Pi Headless - `Missing X server or $DISPLAY` #265

Open tillig opened 1 year ago

tillig commented 1 year ago

I'm working on getting a truly headless install working on a Raspberry Pi 4 using v1.2.2-EDGE. I'm doing all the install and setup work via ssh in a terminal - headless, no display, no VNC.

I downloaded the gSender.Edge-1.2.2-EDGE-armv7l.AppImage file, set it to execute (chmod +x ./gSender...) and I am able to get the help and version information:

pi@longmill:~ $ ./gSender.Edge-1.2.2-EDGE-armv7l.AppImage -- --help
Usage: -- [options]

Options:
  -V, --version                       output the version number
  -p, --port <port>                   Set listen port (default: 0) (default: 0)
  -H, --host <host>                   Set listen address or hostname (default: 127.0.0.1) (default: "127.0.0.1")
  -b, --backlog <backlog>             Set listen backlog (default: 511) (default: 511)
  -c, --config <filename>             Set config file (default: ~/.cncrc)
  -v, --verbose                       Increase the verbosity level (-v, -vv, -vvv)
  -m, --mount <route-path>:<target>   Add a mount point for serving static files (default: [])
  -w, --watch-directory <path>        Watch a directory for changes
  --access-token-lifetime <lifetime>  Access token lifetime in seconds or a time span string (default: 30d)
  --allow-remote-access               Allow remote access to the server (default: false)
  --remote                            Enable Headless mode, exposing the internal server on your local network
  --controller <type>                 Specify CNC controller: Grbl (default: '') (default: "Grbl")
  -h, --help                          output usage information
pi@longmill:~ $ ./gSender.Edge-1.2.2-EDGE-armv7l.AppImage -- --version
1.2.2-EDGE

However, when I try running the app headless, I get a segmentation fault where it appears Electron can't initialize.

pi@longmill:~ $ ./gSender.Edge-1.2.2-EDGE-armv7l.AppImage -- --remote --port 8000 --allow-remote-access
[1919:0115/120342.710845:ERROR:ozone_platform_x11.cc(247)] Missing X server or $DISPLAY
[1919:0115/120342.710979:ERROR:env.cc(226)] The platform failed to initialize.  Exiting.
Segmentation fault

From some light research, it seems the Electron platform is looking for a place to send its display output. However, in a headless mode there is no display output so it seems like this shouldn't be required.

I did try setting $DISPLAY to see if I could get around it, but that doesn't fix it. You still get the "missing X server or $DISPLAY" error.

# Sets $DISPLAY to something like 192.168.1.100:0.0
export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2; exit;}'):0.0

I'm still trying to figure this out. My next step is to try doing some of the steps that Electron has for doing headless testing for continuous integration, like using an in-memory X server.

However this turns out, it'd be nice to either see this in the documentation or have some more intuitive support for the headless system setup.

tillig commented 1 year ago

I've gotten a little further by following those Electron CI instructions.

# Install xvfb
sudo apt-get install xvfb

# Set up the virtual display
export DISPLAY=':99.0'
Xvfb :99 -screen 0 1280x1024x24 > /dev/null 2>&1 &

With that, I'm able to get the app to start, and I can use a browser on my remote computer to access the UI. However, I see a ton of weird errors and warnings:

pi@longmill:~ $ ./gSender.Edge-1.2.2-EDGE-armv7l.AppImage -- --remote --port 8000
[1313:0115/124330.444660:ERROR:viz_main_impl.cc(186)] Exiting GPU process due to errors during initialization
2023-01-15T20:43:33.591Z - info init Loading configuration from "/home/pi/.edge_rc"
(node:1271) Warning: Accessing non-existent property 'padLevels' of module exports inside circular dependency
(Use `gsender --trace-warnings ...` to show where the warning was created)
2023-01-15T20:43:34.270Z - info init Starting the server at http://192.168.1.77:8000
12:43:34.271 › Started remote build at 192.168.1.77:8000 - undefined
Checking for update
[1356:0115/124335.214161:ERROR:viz_main_impl.cc(186)] Exiting GPU process due to errors during initialization
[1384:0115/124335.766295:ERROR:viz_main_impl.cc(186)] Exiting GPU process due to errors during initialization
Error: HttpError: 404
"method: GET url: https://github.com/Sienci-Labs/gsender/releases/download/v1.2.2-edge/latest-linux-arm.yml\n\nPlease double check that your authentication token is correct. Due to security reasons actual status maybe not reported, but 404.\n"
Headers: {
  "server": "GitHub.com",
  "date": "Sun, 15 Jan 2023 20:43:35 GMT",
  "content-type": "text/plain; charset=utf-8",
  "vary": "X-PJAX, X-PJAX-Container, Turbo-Visit, Turbo-Frame, Accept-Encoding, Accept, X-Requested-With",
  "cache-control": "no-cache",
  "strict-transport-security": "max-age=31536000; includeSubdomains; preload",
  "x-frame-options": "deny",
  "x-content-type-options": "nosniff",
  "x-xss-protection": "0",
  "referrer-policy": "no-referrer-when-downgrade",
  "content-security-policy": "default-src 'none'; base-uri 'self'; connect-src 'self'; form-action 'self'; img-src 'self' data:; script-src 'self'; style-src 'unsafe-inline'",
  "content-encoding": "gzip",
  "content-length": "29",
  "x-github-request-id": "902E:5231:7984B30:81F42C1:63C46577"
}
    at createHttpError (/tmp/.mount_gSendeUY4Imk/resources/app/node_modules/builder-util-runtime/out/httpExecutor.js:84:10)
    at ElectronHttpExecutor.handleResponse (/tmp/.mount_gSendeUY4Imk/resources/app/node_modules/builder-util-runtime/out/httpExecutor.js:169:14)
    at ClientRequest.<anonymous> (/tmp/.mount_gSendeUY4Imk/resources/app/node_modules/builder-util-runtime/out/httpExecutor.js:134:16)
    at ClientRequest.emit (node:events:526:28)
    at SimpleURLLoaderWrapper.<anonymous> (node:electron/js2c/browser_init:101:6917)
    at SimpleURLLoaderWrapper.emit (node:events:526:28)
(node:1271) UnhandledPromiseRejectionWarning: HttpError: 404
"method: GET url: https://github.com/Sienci-Labs/gsender/releases/download/v1.2.2-edge/latest-linux-arm.yml\n\nPlease double check that your authentication token is correct. Due to security reasons actual status maybe not reported, but 404.\n"
Headers: {
  "server": "GitHub.com",
  "date": "Sun, 15 Jan 2023 20:43:35 GMT",
  "content-type": "text/plain; charset=utf-8",
  "vary": "X-PJAX, X-PJAX-Container, Turbo-Visit, Turbo-Frame, Accept-Encoding, Accept, X-Requested-With",
  "cache-control": "no-cache",
  "strict-transport-security": "max-age=31536000; includeSubdomains; preload",
  "x-frame-options": "deny",
  "x-content-type-options": "nosniff",
  "x-xss-protection": "0",
  "referrer-policy": "no-referrer-when-downgrade",
  "content-security-policy": "default-src 'none'; base-uri 'self'; connect-src 'self'; form-action 'self'; img-src 'self' data:; script-src 'self'; style-src 'unsafe-inline'",
  "content-encoding": "gzip",
  "content-length": "29",
  "x-github-request-id": "902E:5231:7984B30:81F42C1:63C46577"
}
    at createHttpError (/tmp/.mount_gSendeUY4Imk/resources/app/node_modules/builder-util-runtime/out/httpExecutor.js:84:10)
    at ElectronHttpExecutor.handleResponse (/tmp/.mount_gSendeUY4Imk/resources/app/node_modules/builder-util-runtime/out/httpExecutor.js:169:14)
    at ClientRequest.<anonymous> (/tmp/.mount_gSendeUY4Imk/resources/app/node_modules/builder-util-runtime/out/httpExecutor.js:134:16)
    at ClientRequest.emit (node:events:526:28)
    at SimpleURLLoaderWrapper.<anonymous> (node:electron/js2c/browser_init:101:6917)
    at SimpleURLLoaderWrapper.emit (node:events:526:28)
(node:1271) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
[1401:0115/124336.022980:ERROR:gpu_memory_buffer_support_x11.cc(44)] dri3 extension not supported.
[1321:0115/124336.079376:ERROR:command_buffer_proxy_impl.cc(126)] ContextResult::kTransientFailure: Failed to send GpuControl.CreateCommandBuffer.
2023-01-15T20:43:39.751Z - info service:cncengine No controller found on port  to reconnect to
12:43:46.425 › { address: '192.168.1.77', port: 8000, headless: true }
[1401:0115/124349.590262:ERROR:gl_utils.cc(319)] [.WebGL-0x516b3c00]GL Driver Message (OpenGL, Performance, GL_CLOSE_PATH_NV, High): GPU stall due to ReadPixels
[1401:0115/124352.102890:ERROR:gl_utils.cc(319)] [.WebGL-0x516b3c00]GL Driver Message (OpenGL, Performance, GL_CLOSE_PATH_NV, High): GPU stall due to ReadPixels
[1401:0115/124352.451574:ERROR:gl_utils.cc(319)] [.WebGL-0x516b3c00]GL Driver Message (OpenGL, Performance, GL_CLOSE_PATH_NV, High): GPU stall due to ReadPixels
[1401:0115/124353.236708:ERROR:gl_utils.cc(319)] [.WebGL-0x516b3c00]GL Driver Message (OpenGL, Performance, GL_CLOSE_PATH_NV, High): GPU stall due to ReadPixels (this message will no longer repeat)

Despite the errors and warnings, I am able to connect. I was able to connect to the remote UI and then tell it to connect to my machine. I uploaded a simple .nc file and the visualizer seemed to work. Jogging seems to work. I haven't had a chance to actually cut anything yet, I admit I have some trepidation since I don't get a ton of time to play CNC and right now I've spent quite a bit of time already just getting this to work; I don't have time to mess up the cuts I was hoping to get done, like if I do something in the middle of a cut that will cause the app to terminate or Xvfb to die. I also admit a lot of that trepidation comes from the raft of warnings and errors at startup. I'd probably be more willing to just hit the ol' GO button if I didn't see errors around web requests and GPU transient failures.

I found this StackOverflow question that indicated I might be able to start Xvfb with +extension GLX +render and get past the GPU errors there, but no dice. It also seemed to indicate that I might have to pass some sort of --ignore-gpu-blacklist to Electron but I'm not sure how to do that.

I found a few other references to WebGL and headless/CI sorts of systems and in some cases the use of those APIs needed to be explicitly disabled for those cases. I'm not sure how possible that would be here.

Likely before I'd cut anything I'd also need to figure out how to make this headless setup start as a service so I don't have to interactively run an SSH session. If the SSH session gets dropped (which has happened when I switched my laptop from wired to wireless while messing with this) then gSender stops working.

Cjkeenan commented 1 year ago

Has there been any development on this front? I think that with the inclusion of --remote mode, this kind of thing would go hand in hand.

Pastitas commented 1 year ago

It's outputing the same now, It's a shame this is not supported since CNCjs does.

tillig commented 1 year ago

I haven't figured it out myself, and I won't lie - looking at how the pull request queue sits unreviewed, it doesn't motivate me to dive in and figure it out just to have the PR ignored, so I haven't invested any more time. I keep waiting to see progress in the release notes before coming back to gSender.

Pastitas commented 1 year ago

I'd think since they have a cnc main-board that includes a screen-less Rpi they might be already working on it, but it's unclear why they wouldn't say anything to this issue or review PR's but maybe they've got their hands full now.

Bobbydigital420 commented 1 year ago

I was able to get it running headless. You will still need to run a desktop environment but can leave all peripherals disconnected once you get it all setup. Here's the steps I did.

Create a desktop autostart in /etc/xdg/autostart/ and add the following to it. You can name it anything you like just use the .desktop extension.

[Desktop Entry] Type=Application Name=gSender Comment=gSender NoDisplay=false Exec=/home/pi/gSender-1.2.2-armv7l.AppImage --remote NotShowIn=GNOME;KDE;XFCE;

Under Exec be sure to change the path/filename to where your gSender AppImage is located.

For the life of me I couldn't figure out why the LightDM display manager wouldn't start with the system running headless. I plugged in a monitor and LightDM finally started and it booted into the desktop environment. I was able to find out after a little searching that you can get the display manager running without a monitor by doing the following.

sudo raspi-config

2 Display Options > D1 Resolution

Then set any resolution that is not the connected monitors default resolution. I just set mine to 720P 60Hz just incase I ever need to remote desktop into the Raspberry Pi.

Also make sure you set auto login to desktop. This can be done from the raspi-config menu under

1 System Options > S5 Boot/Auto Login > B4 Desktop

This will enable you to ditch the monitor. Not sure if connecting a monitor is necessary for 2 Display Options to show up in raspi-config but it may be, if not then you can probably set it all up completely headless after a fresh flash of Raspbian onto a SD card.

I know this isn't a truly headless setup with an Xorg/desktop environment running but figured someone may want to use this setup. This will likely only run on 2GB Pi models as memory usage was at about 600-700MB. You could further lower the resources likely by doing a custom xsession that just starts gSender.

Pastitas commented 8 months ago

Maybe openbox is a good option for this, as it's a very low memory footprint DE Is this gonna get any attention?? at least a word from the development team would be very nice

J-eremy commented 7 months ago

Maybe openbox is a good option for this, as it's a very low memory footprint DE Is this gonna get any attention?? at least a word from the development team would be very nice

You can build the whole thing yourself and get the whole app unpacked. image

Or, you can use the files in the dist directory before you build it for the platform directly with node, and bypass the whole electron package completely.

image

When you do the build steps, right before you do the build:platform step all the things you need will be in the dist directory of the project directory.

It will run the server completely headless that you can connect to, but it is running in a development type state so it may just be easier to wait for them to implement a more streamline way and official way to do it. image

Either way, the functionality is there its just going to take some massaging from the devs, but time is the most valuable resource. image image image

The other issue is that gSender is using a prehistoric version of node.js. They are currently still developing on node 14 because there are a TON of obsolete packages being used that aren't exactly forward compatible, so again that comes down to time. In my opinion this software is absolutely awesome, but needs a complete overhaul to modernity. I assume the underlying issue here is the fact that this is technically a branch of cnc.js so the base of the code is heavily shared and outdated.

TLDR: What your asking may be more complicated than you think.