|
@@ -432,7 +432,11 @@ class SimpleMQTTServer:
|
|
|
disconnected.append(client_id)
|
|
disconnected.append(client_id)
|
|
|
continue
|
|
continue
|
|
|
serial = self._client_serials.get(client_id, self.serial)
|
|
serial = self._client_serials.get(client_id, self.serial)
|
|
|
- await self._send_status_report(writer, serial=serial)
|
|
|
|
|
|
|
+ # log_event=False: the 1Hz cached push is already
|
|
|
|
|
+ # captured by ``dump_wire`` snapshot mode (see
|
|
|
|
|
+ # _debug.py); appending it to the cmd.jsonl would
|
|
|
|
|
+ # flood the file ~60 lines/min per VP.
|
|
|
|
|
+ await self._send_status_report(writer, serial=serial, log_event=False)
|
|
|
push_counts[client_id] = push_counts.get(client_id, 0) + 1
|
|
push_counts[client_id] = push_counts.get(client_id, 0) + 1
|
|
|
except OSError as e:
|
|
except OSError as e:
|
|
|
logger.debug("Failed to push status to %s: %s", client_id, e)
|
|
logger.debug("Failed to push status to %s: %s", client_id, e)
|
|
@@ -846,7 +850,9 @@ class SimpleMQTTServer:
|
|
|
except (IndexError, ValueError, OSError) as e:
|
|
except (IndexError, ValueError, OSError) as e:
|
|
|
logger.debug("MQTT SUBSCRIBE error: %s", e)
|
|
logger.debug("MQTT SUBSCRIBE error: %s", e)
|
|
|
|
|
|
|
|
- async def _send_status_report(self, writer: asyncio.StreamWriter, serial: str | None = None) -> None:
|
|
|
|
|
|
|
+ async def _send_status_report(
|
|
|
|
|
+ self, writer: asyncio.StreamWriter, serial: str | None = None, log_event: bool = True
|
|
|
|
|
+ ) -> None:
|
|
|
"""Send a status report to the slicer after connection.
|
|
"""Send a status report to the slicer after connection.
|
|
|
|
|
|
|
|
When a bridge is active and has cached the real printer's latest
|
|
When a bridge is active and has cached the real printer's latest
|
|
@@ -915,7 +921,7 @@ class SimpleMQTTServer:
|
|
|
print_block["print_error"] = 0
|
|
print_block["print_error"] = 0
|
|
|
status = {"print": print_block}
|
|
status = {"print": print_block}
|
|
|
dump_wire(self.vp_name, "out", status)
|
|
dump_wire(self.vp_name, "out", status)
|
|
|
- await self._publish_to_report(writer, status, serial or self.serial)
|
|
|
|
|
|
|
+ await self._publish_to_report(writer, status, serial or self.serial, log_event=log_event)
|
|
|
return
|
|
return
|
|
|
|
|
|
|
|
# No bridge / no cache yet — fall back to the synthetic stub.
|
|
# No bridge / no cache yet — fall back to the synthetic stub.
|
|
@@ -992,7 +998,7 @@ class SimpleMQTTServer:
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- await self._publish_to_report(writer, status, serial or self.serial)
|
|
|
|
|
|
|
+ await self._publish_to_report(writer, status, serial or self.serial, log_event=log_event)
|
|
|
|
|
|
|
|
except OSError as e:
|
|
except OSError as e:
|
|
|
logger.error("Failed to send status report: %s", e)
|
|
logger.error("Failed to send status report: %s", e)
|
|
@@ -1088,13 +1094,24 @@ class SimpleMQTTServer:
|
|
|
self._current_file = filename
|
|
self._current_file = filename
|
|
|
self._prepare_percent = prepare_percent
|
|
self._prepare_percent = prepare_percent
|
|
|
|
|
|
|
|
- async def _publish_to_report(self, writer: asyncio.StreamWriter, payload: dict, serial: str = "") -> None:
|
|
|
|
|
|
|
+ async def _publish_to_report(
|
|
|
|
|
+ self, writer: asyncio.StreamWriter, payload: dict, serial: str = "", log_event: bool = True
|
|
|
|
|
+ ) -> None:
|
|
|
"""Publish a message on the device report topic.
|
|
"""Publish a message on the device report topic.
|
|
|
|
|
|
|
|
Real Bambu printers wire-format push_status JSON with 4-space indentation
|
|
Real Bambu printers wire-format push_status JSON with 4-space indentation
|
|
|
(32254 bytes for an idle H2D push vs 14268 bytes compact). BambuStudio's
|
|
(32254 bytes for an idle H2D push vs 14268 bytes compact). BambuStudio's
|
|
|
Send pre-flight rejects compact JSON — without matching the on-wire
|
|
Send pre-flight rejects compact JSON — without matching the on-wire
|
|
|
format the slicer never proceeds to FTP upload.
|
|
format the slicer never proceeds to FTP upload.
|
|
|
|
|
+
|
|
|
|
|
+ ``log_event=True`` records the publish in ``vp_wire/<vp>_cmd.jsonl``
|
|
|
|
|
+ under the ``bridge_to_slicer`` direction so #1622-style triages can
|
|
|
|
|
+ diff the bridge's own outbound replies (info.get_version answer,
|
|
|
|
|
+ project_file ack, on-demand pushall response) against the real
|
|
|
|
|
+ printer's ``printer_to_slicer`` forwards. The 1Hz periodic push
|
|
|
|
|
+ sets ``log_event=False`` because dump_wire's overwrite-snapshot
|
|
|
|
|
+ already covers cache shape and a per-second JSONL line would dwarf
|
|
|
|
|
+ the actual command events.
|
|
|
"""
|
|
"""
|
|
|
topic = f"device/{serial or self.serial}/report"
|
|
topic = f"device/{serial or self.serial}/report"
|
|
|
message = json.dumps(payload, indent=4)
|
|
message = json.dumps(payload, indent=4)
|
|
@@ -1116,6 +1133,16 @@ class SimpleMQTTServer:
|
|
|
packet += topic_bytes
|
|
packet += topic_bytes
|
|
|
packet += message_bytes
|
|
packet += message_bytes
|
|
|
|
|
|
|
|
|
|
+ if log_event:
|
|
|
|
|
+ # Env-flagged command trace (#1622): captures bridge-synthesised
|
|
|
|
|
+ # replies (info.get_version, project_file ack, on-demand pushall
|
|
|
|
|
+ # response) AFTER the payload is finalised but before it hits
|
|
|
|
|
+ # the wire — so the cmd.jsonl reflects exactly what the slicer
|
|
|
|
|
+ # parses. Pair with the slicer_to_bridge events from
|
|
|
|
|
+ # _handle_publish and the printer_to_slicer fan-outs from
|
|
|
|
|
+ # mqtt_bridge.
|
|
|
|
|
+ append_event(self.vp_name, "bridge_to_slicer", topic, payload)
|
|
|
|
|
+
|
|
|
writer.write(packet)
|
|
writer.write(packet)
|
|
|
# Timeout the drain to prevent blocking the event loop if the
|
|
# Timeout the drain to prevent blocking the event loop if the
|
|
|
# MQTT client stops reading (e.g. slicer busy with FTP upload).
|
|
# MQTT client stops reading (e.g. slicer busy with FTP upload).
|