Moonraker - metascan

Im trying to refresh the metadata of gcode files with a little Python script (more or less shamelessly copied from moontest):

#!/usr/bin/python3
import argparse
import asyncio
import json
import pathlib

SOCKET_FILE = "~/printer_data/comms/moonraker.sock"

async def send_rpc_request(sockpath, filename):
    """Send an RPC request to the Moonraker server."""
    rpc_message = {
        "jsonrpc": "2.0",
        "method": "server.files.metascan",
        "params": {
            "filename": filename
        },
        "id": 354789464
    }
    message = json.dumps(rpc_message).encode() + b"\x03"
    print("Request:", message)
    reader, writer = await asyncio.open_unix_connection(sockpath)
    try:
        writer.write(message)
        await writer.drain()
        response = await reader.readuntil(b'\x03')
        print("Response:", response.decode().strip())
    finally:
        writer.close()
        await writer.wait_closed()

def main():
    parser = argparse.ArgumentParser(description="Send RPC to Moonraker via Unix socket")
    parser.add_argument("filename", help="Name of the file to be scanned")
    args = parser.parse_args()

    sockpath = pathlib.Path(SOCKET_FILE).expanduser()
    asyncio.run(send_rpc_request(sockpath, args.filename))

if __name__ == "__main__":
    main()

Apparently I’m messing it up somewhere, as I do not get the intended response:

$ python refresh_meta.py external/test.gcode
Request: b'{"jsonrpc": "2.0", "method": "server.files.metascan", "params": {"filename": "external/test.gcode"}, "id": 354789464}\x03'
Response: {"jsonrpc":"2.0","method":"notify_proc_stat_update","params":[{"moonraker_stats":{"time":1733856121.3238893,"cpu_usage":5.82,"memory":55000,"mem_units":"kB"},"cpu_temp":49.229,"network":{"lo":{"rx_bytes":63185971,"tx_bytes":63185971,"rx_packets":99095,"tx_packets":99095,"rx_errs":0,"tx_errs":0,"rx_drop":0,"tx_drop":0,"bandwidth":20513.49},"end0":{"rx_bytes":3489977,"tx_bytes":29563572,"rx_packets":45785,"tx_packets":42069,"rx_errs":0,"tx_errs":0,"rx_drop":3386,"tx_drop":0,"bandwidth":8035.96},"can0":{"rx_bytes":402083,"tx_bytes":21327,"rx_packets":55654,"tx_packets":3581,"rx_errs":0,"tx_errs":0,"rx_drop":0,"tx_drop":0,"bandwidth":143.86},"wlan0":{"rx_bytes":0,"tx_bytes":0,"rx_packets":0,"tx_packets":0,"rx_errs":0,"tx_errs":0,"rx_drop":0,"tx_drop":0,"bandwidth":0.0}},"system_cpu_usage":{"cpu":24.81,"cpu0":10.31,"cpu1":7.14,"cpu2":73.74,"cpu3":6.06},"system_memory":{"total":940788,"available":527248,"used":413540},"websocket_connections":4}]}

@Arksine, I’d appreciate your guidance.

Edit:
Shame on me - moonraker.log attached

moonraker(15).log (26.4 KB)

I don’t have Moonraker installed to test, but just on a surface level skim of the test script I don’t see any reason the first thing you read from the socket would necessarily be the response to the request. If I’m following the logic correctly you should instead read lines until the returned data has an id matching the request you sent: moontest/scripts/unix_socket_test.py at 72a89239f34ca773e5526810cb6034dc5e6d714a · Arksine/moontest · GitHub

Thanks @flowerysong
My interpretation of moontest/scripts/unix_socket_test.py at 72a89239f34ca773e5526810cb6034dc5e6d714a · Arksine/moontest · GitHub would have been that the entire data is returned up to the EOT and the id being a subset of this data.

Right, but the EOT signals the end of an individual response. Since the first one you’re getting is not the response you’re interested in, you have to read up to the EOT at least one more time.

1 Like

Ahh, now I understand this piece of code. Many thanks @flowerysong, it works.

#!/usr/bin/python3
import argparse
import asyncio
import json
import pathlib

SOCKET_FILE = "~/printer_data/comms/moonraker.sock"

async def send_rpc_request(sockpath, filename):
    """Send an RPC request to the Moonraker server."""
    rpc_message = {
        "jsonrpc": "2.0",
        "method": "server.files.metascan",
        "params": {
            "filename": filename
        },
        "id": 3545
    }
    message = json.dumps(rpc_message).encode() + b"\x03"
    print("Request:", message)

    reader, writer = await asyncio.open_unix_connection(sockpath)
    try:
        writer.write(message)
        await writer.drain()

        while not reader.at_eof():
            try:
                response = await reader.readuntil(b"\x03")
                decoded = response[:-1].decode("utf-8")
                item = json.loads(decoded)

                if item.get("id") == rpc_message["id"]:
                    print("Response:", json.dumps(item, indent=4))
                    break
                else:
                    print("Unmatched Response or Notification:", json.dumps(item, indent=4))

            except asyncio.IncompleteReadError:
                print("Connection closed before receiving complete response.")
                break
            except Exception as e:
                print(f"Error processing response: {e}")
                break

    finally:
        writer.close()
        await writer.wait_closed()

def main():
    parser = argparse.ArgumentParser(description="Send RPC to Moonraker via Unix socket")
    parser.add_argument("filename", help="Name of the file to be scanned")
    args = parser.parse_args()

    sockpath = pathlib.Path(SOCKET_FILE).expanduser()
    asyncio.run(send_rpc_request(sockpath, args.filename))

if __name__ == "__main__":
    main()
$ python refresh_meta.py external/test2.gcode
Unmatched Response or Notification: {
    "jsonrpc": "2.0",
    "method": "notify_proc_stat_update",
    "params": [
        {
            "moonraker_stats": {
                "time": 1733859391.9836972,
                "cpu_usage": 4.89,
                "memory": 55296,
                "mem_units": "kB"
            },
            "cpu_temp": 51.659,
            "network": {
                "lo": {
                    "rx_bytes": 151400632,
                    "tx_bytes": 151400632,
                    "rx_packets": 180047,
                    "tx_packets": 180047,
                    "rx_errs": 0,
                    "tx_errs": 0,
                    "rx_drop": 0,
                    "tx_drop": 0,
                    "bandwidth": 13378.62
                },
                "end0": {
                    "rx_bytes": 57504230,
                    "tx_bytes": 48881665,
                    "rx_packets": 114673,
                    "tx_packets": 72643,
                    "rx_errs": 0,
                    "tx_errs": 0,
                    "rx_drop": 6680,
                    "tx_drop": 0,
                    "bandwidth": 4322.68
                },
                "can0": {
                    "rx_bytes": 799435,
                    "tx_bytes": 41259,
                    "rx_packets": 110606,
                    "tx_packets": 6903,
                    "rx_errs": 0,
                    "tx_errs": 0,
                    "rx_drop": 0,
                    "tx_drop": 0,
                    "bandwidth": 114.89
                },
                "wlan0": {
                    "rx_bytes": 0,
                    "tx_bytes": 0,
                    "rx_packets": 0,
                    "tx_packets": 0,
                    "rx_errs": 0,
                    "tx_errs": 0,
                    "rx_drop": 0,
                    "tx_drop": 0,
                    "bandwidth": 0.0
                }
            },
            "system_cpu_usage": {
                "cpu": 37.18,
                "cpu0": 70.0,
                "cpu1": 6.38,
                "cpu2": 9.18,
                "cpu3": 62.38
            },
            "system_memory": {
                "total": 940788,
                "available": 516520,
                "used": 424268
            },
            "websocket_connections": 3
        }
    ]
}
Response: {
    "jsonrpc": "2.0",
    "result": {
        "size": 28052757,
        "modified": 1733857575.1,
        "uuid": "a9fa054a-f278-4afb-b1f8-6449f46ab7b6",
        "slicer": "OrcaSlicer",
        "slicer_version": "2.2.0",
        "gcode_start_byte": 31537,
        "gcode_end_byte": 28035696,
        "layer_count": 125,
        "object_height": 20.04,
        "estimated_time": 14214,
        "nozzle_diameter": 0.4,
        "layer_height": 0.16,
        "first_layer_height": 0.2,
        "first_layer_extr_temp": 215.0,
        "first_layer_bed_temp": 70.0,
        "chamber_temp": 0.0,
        "filament_name": "Sunlu PLA+ White",
        "filament_type": "PLA",
        "filament_total": 30842.83,
        "filament_weight_total": 91.99,
        "thumbnails": [
            {
                "width": 32,
                "height": 32,
                "size": 1159,
                "relative_path": ".thumbs/test2-32x32.png"
            },
            {
                "width": 48,
                "height": 48,
                "size": 1483,
                "relative_path": ".thumbs/test2-48x48.png"
            },
            {
                "width": 300,
                "height": 300,
                "size": 20013,
                "relative_path": ".thumbs/test2-300x300.png"
            }
        ],
        "print_start_time": null,
        "job_id": null,
        "filename": "external/test2.gcode"
    },
    "id": 3545
}

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.