mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-26 10:21:00 +00:00
feat(orca-agent): integrate OrcaSonar AMS synchronization
- Translate slicer AMS commands to OrcaSonar’s canonical write format - Gate AMS operations using printer capabilities - Refresh capabilities when filament topology becomes available - Apply virtual-slot removals only from full status snapshots - Add synchronization tests and document the integration
This commit is contained in:
@@ -394,6 +394,20 @@ TEST_CASE("OrcaPrinterAgent rewrites Bambu ams_* payloads onto the canonical Orc
|
||||
canon("dev-c2", R"({"print":{"command":"ams_change_filament","selector":"external"}})", &unsupported);
|
||||
CHECK(unsupported);
|
||||
|
||||
// A lane can resolve to an external slot server-side (§7.8), so a device
|
||||
// that declares only external still admits a canonical lane write.
|
||||
Slic3r::register_ams_ops("dev-c4", {"external"});
|
||||
unsupported = false;
|
||||
canon("dev-c4", R"({"print":{"command":"ams_change_filament","selector":"lane","lane":1}})", &unsupported);
|
||||
CHECK(!unsupported);
|
||||
unsupported = false;
|
||||
canon("dev-c4", R"({"print":{"command":"ams_change_filament","selector":"external"}})", &unsupported);
|
||||
CHECK(!unsupported);
|
||||
// Other selectors still gate on their own token.
|
||||
unsupported = false;
|
||||
canon("dev-c4", R"({"print":{"command":"ams_change_filament","selector":"unload"}})", &unsupported);
|
||||
CHECK(unsupported);
|
||||
|
||||
// A device with no capabilities record is never gated (server backstops).
|
||||
unsupported = false;
|
||||
canon("dev-c3", R"({"print":{"command":"ams_user_setting","ams_id":0}})", &unsupported);
|
||||
@@ -441,3 +455,72 @@ TEST_CASE("an AMS tray selection sends the tray's ams_id and slot_id", "[OrcaPri
|
||||
CHECK(body["print"]["ams_id"] == 0);
|
||||
CHECK(body["print"]["slot_id"] == 3);
|
||||
}
|
||||
|
||||
// A filament frame can arrive after the get_capabilities reply was missed (the
|
||||
// topology resolved late, or Klipper restarted). While the device is still
|
||||
// unconfirmed, one filament frame must re-ask, throttled, and stop once a reply
|
||||
// arrives so the session can leave FilamentSyncMode::none.
|
||||
TEST_CASE("a filament frame re-requests capabilities while the topology is unconfirmed", "[OrcaPrinterAgent]") {
|
||||
struct RefreshProbe : OrcaPrinterAgent {
|
||||
using OrcaPrinterAgent::OrcaPrinterAgent;
|
||||
using OrcaPrinterAgent::deliver_to_sink;
|
||||
std::vector<std::string> refreshes;
|
||||
void request_filament_capabilities(const std::string& dev_id, bool /*local*/) override {
|
||||
refreshes.push_back(dev_id);
|
||||
}
|
||||
} agent("/tmp");
|
||||
agent.set_on_message_fn([](std::string, std::string) {});
|
||||
|
||||
const std::string frame = R"({"print":{"command":"push_status","vir_slot":[{"id":"255"}]}})";
|
||||
agent.deliver_to_sink("dev-refresh-1", frame, /*local=*/true);
|
||||
REQUIRE(agent.refreshes.size() == 1);
|
||||
|
||||
// Throttled: a second frame immediately after does not ask again.
|
||||
agent.deliver_to_sink("dev-refresh-1", frame, /*local=*/true);
|
||||
CHECK(agent.refreshes.size() == 1);
|
||||
|
||||
// A capability reply confirms the topology: no further re-request.
|
||||
agent.deliver_to_sink("dev-refresh-1",
|
||||
R"({"info":{"command":"get_capabilities","capabilities":{"protocol":{"features":{"filament_slots":true}}}}})",
|
||||
/*local=*/true);
|
||||
agent.deliver_to_sink("dev-refresh-1", frame, /*local=*/true);
|
||||
CHECK(agent.refreshes.size() == 1);
|
||||
|
||||
// A status without filament state never triggers a request.
|
||||
agent.deliver_to_sink("dev-refresh-2", R"({"print":{"command":"push_status","mc_percent":10}})", /*local=*/true);
|
||||
CHECK(agent.refreshes.size() == 1);
|
||||
}
|
||||
|
||||
// Deselecting a cloud device forgets its declaration, so a later session starts
|
||||
// from "no reply yet" instead of a stale one. With no record an undeclared op is
|
||||
// admitted again, which is how a freshly selected device starts.
|
||||
TEST_CASE("deselecting a cloud device forgets its capabilities", "[OrcaPrinterAgent]") {
|
||||
OrcaPrinterAgent agent("/tmp");
|
||||
Slic3r::register_ams_ops("dev-cloud-clear", {"change_filament"});
|
||||
Slic3r::register_filament_slots("dev-cloud-clear", true);
|
||||
CHECK_FALSE(Slic3r::ams_op_supported("dev-cloud-clear", "external"));
|
||||
|
||||
agent.set_user_selected_machine("dev-cloud-clear");
|
||||
agent.set_user_selected_machine("");
|
||||
|
||||
CHECK(Slic3r::ams_op_supported("dev-cloud-clear", "external"));
|
||||
CHECK_FALSE(Slic3r::has_filament_slots("dev-cloud-clear"));
|
||||
}
|
||||
|
||||
// A LAN feed for the same device id must keep its declaration when the cloud
|
||||
// selection is cleared: clearing it would strand an independently live session.
|
||||
// why hidden: spawns the LAN connect worker, like the other connect tests.
|
||||
TEST_CASE("deselecting a cloud device keeps a live LAN declaration", "[OrcaPrinterAgent][.integration]") {
|
||||
OrcaPrinterAgent agent("/tmp");
|
||||
Slic3r::register_ams_ops("dev-lan-keep", {"change_filament"});
|
||||
Slic3r::register_filament_slots("dev-lan-keep", true);
|
||||
|
||||
REQUIRE(agent.connect_printer("dev-lan-keep", "10.255.255.1", "orcasonar", "code", false) == BAMBU_NETWORK_SUCCESS);
|
||||
agent.set_user_selected_machine("dev-lan-keep");
|
||||
agent.set_user_selected_machine("");
|
||||
|
||||
CHECK_FALSE(Slic3r::ams_op_supported("dev-lan-keep", "external"));
|
||||
CHECK(Slic3r::has_filament_slots("dev-lan-keep"));
|
||||
|
||||
agent.disconnect_printer();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user