mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-19 15:47:04 +02:00
fix(vp): emit FINISH after FTP upload so Print-flow slicers unwedge (#1280)
Bambuddy's VP supports two slicer flows: Send (file upload only — what queue/immediate/review modes are designed for) and Print (file upload + start-print, intended for proxy mode). When a user clicks Print against a non-proxy mode the VP must still respond gracefully — the file is fine to receive, just the start-print never happens. Instead the slicer wedged at "Downloading...(0%)" and blocked the next dispatch with "The printer is busy with another print job". Cause: on_file_received transitioned gcode_state PREPARE -> IDLE directly. Print-flow slicers watch the state cycle and only release their in-flight-job lock on PREPARE -> ... -> FINISH (or FAILED). PREPARE -> IDLE looks like "printer abandoned my job" and keeps the prior job pinned in the slicer's memory. Fix: transition PREPARE -> FINISH with prepare_percent=100. The 1-Hz periodic status push broadcasts the new state to every connected slicer within a second. Send-flow slicers don't watch this state so the change is a no-op for them; Print-flow slicers see the FINISH they were waiting for and unwedge.
This commit is contained in:
@@ -171,6 +171,41 @@ class TestVirtualPrinterInstance:
|
||||
|
||||
mock_archive.assert_called_once_with(file_path, "192.168.1.100")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_on_file_received_signals_FINISH_to_slicer(self, instance):
|
||||
"""Regression #1280: when a slicer's Print flow uploads to a non-proxy VP,
|
||||
the VP must transition gcode_state PREPARE → FINISH so the slicer's
|
||||
in-flight-job lock releases. Going PREPARE → IDLE wedges Orca at
|
||||
"Downloading...(0%)" and blocks the next dispatch with "busy with
|
||||
another print job".
|
||||
|
||||
Send-flow slicers don't watch the post-upload state, so this is a
|
||||
no-op behavior change for them.
|
||||
"""
|
||||
instance.mode = "immediate"
|
||||
instance._mqtt = MagicMock()
|
||||
instance._mqtt.set_gcode_state = MagicMock()
|
||||
file_path = Path("/tmp/test.3mf") # nosec B108
|
||||
|
||||
with patch.object(instance, "_archive_file", new_callable=AsyncMock):
|
||||
await instance.on_file_received(file_path, "192.168.1.100")
|
||||
|
||||
instance._mqtt.set_gcode_state.assert_called_once_with("FINISH", filename="test.3mf", prepare_percent="100")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_on_file_received_non_3mf_does_not_touch_state(self, instance):
|
||||
"""Non-3MF uploads (e.g., a job's auxiliary files) must not transition
|
||||
the visible state — the slicer is only tracking the .3mf upload."""
|
||||
instance.mode = "immediate"
|
||||
instance._mqtt = MagicMock()
|
||||
instance._mqtt.set_gcode_state = MagicMock()
|
||||
file_path = Path("/tmp/test.gcode") # nosec B108
|
||||
|
||||
with patch.object(instance, "_archive_file", new_callable=AsyncMock):
|
||||
await instance.on_file_received(file_path, "192.168.1.100")
|
||||
|
||||
instance._mqtt.set_gcode_state.assert_not_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_archive_file_skips_non_3mf(self, instance):
|
||||
"""Verify non-3MF files are skipped and cleaned up."""
|
||||
|
||||
Reference in New Issue
Block a user