Komenda wysłana. Czy urządzenie naprawdę ją wykonało?
Jak zaprojektować sterowanie urządzeniem, które odróżnia przyjęcie komendy od ruchu, odzyskuje wynik po utracie odpowiedzi i jasno pokazuje stan po restarcie.

Operator zadaje pozycję 40 mm. Panel wyświetla „Gotowe”, ale wózek napędu stoi w połowie drogi. Co system faktycznie potwierdził? Serwer mógł przyjąć żądanie, broker przekazać wiadomość, a sterownik wpisać zadanie do kolejki. Każdy z tych etapów mówi coś innego.
Prześledzimy fikcyjną komendę C42 dla napędu liniowego. Najpierw zgubimy odpowiedź serwera, a potem sprawdzimy, co się stanie po restarcie sterownika. Przyjęte wartości służą do pokazania kontraktu aplikacji. Ich dobór do konkretnego napędu wymaga pomiarów i prób.
Co naprawdę potwierdza panel
Panel wysyła POST /devices/D1/commands z identyfikatorem C42, celem 40 mm, terminem rozpoczęcia i oczekiwaną generacją sterowania 7. W tym kontrakcie API w jednej transakcji zapisuje komendę oraz zadanie jej wysłania. Po zatwierdzeniu transakcji zwraca 202 Accepted z adresem /devices/D1/commands/C42, pod którym panel może odczytać stan. HTTP 202 sygnalizuje przyjęcie do przetwarzania. Znaczenie trwałego zapisu wynika tu z kontraktu API. RFC 9110, § 15.3.3
W razie potrzeby przewiń tabelę poziomo.
| Stan | Co system potrafi wykazać |
|---|---|
Przyjęta · accepted |
API trwale zapisało C42 i zadanie wysyłki. |
Wykonywana · executing |
Sterownik zgłosił rozpoczęcie wykonania C42. |
Potwierdzona · confirmed |
Dowód powiązany z C42 spełnił kryteria położenia, jakości i świeżości. |
Wygasła · expired |
Sterownik albo autorytatywna kolejka ustalili, że upłynął termin startu, zanim rozpoczęto wykonanie. |
Błąd · failed |
Sterownik przekazał rozstrzygający błąd wykonania. Wózek mógł wykonać część ruchu. |
Nieznany efekt · unknown |
Dostępne informacje nie rozstrzygają, jak zakończyła się C42. |
Panel pokazuje osobno cel komendy, jej ostatni znany stan i ostatnie zmierzone położenie wraz z wiekiem odczytu. Historyczne potwierdzenie C42 pozostaje prawdziwe, nawet jeśli wózek później zmienił położenie. Przy utracie łączności panel może oznaczyć zapisany stan jako nieaktualny. Stan unknown opisuje natomiast wynik nierozstrzygnięty przez system, np. po restarcie w trakcie ruchu.
Jak komenda przechodzi przez system
Diagram zaczyna się po przyjęciu C42. Odmowa autoryzacji lub konflikt przy próbie utworzenia komendy następują wcześniej. Strzałka od nieznanego efektu do potwierdzenia oznacza odzyskanie dowodu dotyczącego tej samej próby, a strzałka do błędu oznacza odzyskanie rozstrzygającego zapisu awarii. Sam upływ czasu bez danych nie przesądza, czy wózek się poruszył.
API zapisuje stan osobno dla każdej komendy i nadaje mu kolejną wersję po przyjęciu dozwolonego przejścia. Panel może wtedy pominąć starszą wersję C42, a zdarzenie C42 nie zmienia stanu C43. Licznik zdarzeń sterownika może zacząć się od nowa po restarcie. API porównuje te numery tylko w obrębie jednego uruchomienia i sprawdza, czy zdarzenie dotyczy właściwej próby. Spóźnione „Wykonywana” nie cofa „Potwierdzona”.
Jak odzyskać wynik bez drugiego ruchu
Połączenie z panelem zrywa się po zapisaniu C42, zanim panel otrzyma 202. Sterownik w międzyczasie dostaje zlecenie, wykonuje ruch i odsyła dowód osiągnięcia celu. Gdy panel wraca do sieci, ponawia ten sam POST z C42. API znajduje zapis i zwraca 200 OK z aktualnym stanem confirmed. Jeżeli stan w chwili ponowienia byłby wcześniejszy, odpowiedź zawierałaby właśnie ten stan. Jeżeli panel zna już adres C42, może odczytać stan przez GET.
Identyfikator powstaje raz na intencję operatora. Dwa żądania z tym samym identyfikatorem i tymi samymi parametrami wskazują ten sam zasób. Próba ponowienia C42 z celem 60 mm kończy się konfliktem. Ustal zakres klucza, np. konto i urządzenie, oraz sprawdzaj uprawnienia także podczas odczytu zapisanego wyniku. AWS Builders’ Library
Są tu dwie osobne granice ponowienia.
- Panel i API. Unikalność klucza i atomowy zapis komendy z zadaniem wysyłki zapobiegają utworzeniu dwóch zleceń, nawet przy równoległych żądaniach. Zadanie przechowywane w tej samej bazie to przykład wzorca transactional outbox. AWS Prescriptive Guidance
- Wysyłka do sterownika. Proces może wysłać C42, stracić połączenie przed oznaczeniem zadania jako wysłanego i wysłać je ponownie. Sterownik rozpoznaje identyfikator w trwałym zapisie i w sposób atomowy rezerwuje rozpoczęcie wykonania. MQTT QoS 2 dotyczy dostarczenia wiadomości w ramach protokołu, a idempotencja metody HTTP dotyczy zamierzonego efektu na serwerze. Liczbę ruchów rozstrzyga kontrakt wykonania na urządzeniu. MQTT 5.0, § 4.3 · RFC 9110, § 9.2.2
Okres przechowywania identyfikatorów musi obejmować dopuszczalne ponowienia, oczekiwanie w kolejkach i powroty po przerwie. Po wyczerpaniu budżetu prób panel pokazuje ostatni znany stan. Automatyczne nadanie nowego identyfikatora utworzyłoby kolejną intencję. Odczyt istniejącego C42 może być dostępny także po terminie rozpoczęcia ruchu, zgodnie z retencją i aktualnymi uprawnieniami.
Co zmienia restart napędu
Sterownik zapisuje rozpoczęcie wykonania przed podaniem sygnału na napęd. Zapis i fizyczny ruch to osobne działania. Jeśli zasilanie zaniknie między nimi lub w trakcie ruchu, po restarcie ten sam zapis może oznaczać brak ruchu, ruch częściowy albo osiągnięcie celu bez zapisu wyniku. System przechodzi do unknown i nie wznawia C42 automatycznie.
Sterownik może przekazać zachowany wynik z poprzedniego uruchomienia, jeśli dowód wiąże go z C42 i można zweryfikować kryteria obowiązujące podczas próby. Nowy odczyt 40 mm po restarcie jest osobną obserwacją. Pokazuje aktualne położenie, lecz sam w sobie nie rozstrzyga historii C42. To samo dotyczy celu bezwzględnego. Przed ponownym ruchem trzeba ustalić m.in. aktualną pozycję zerową, zmiany mechanizmu i kolejność zadań. W przypadku komendy względnej „przesuń o 40 mm” powtórzenie może dodać kolejne 40 mm.
Jaki odczyt wystarczy do potwierdzenia
W przykładzie mierzymy położenie wózka. Pozycja silnika może się zgadzać mimo luzu lub uszkodzenia sprzęgła. Sterownik akceptuje wartości od 39,5 do 40,5 mm i wiek próbki od 0 do 200 ms, włącznie z granicami. Wiek liczy na zegarze monotonicznym z tego samego uruchomienia. Próbka musi pochodzić z chwili rozpoczęcia wykonania lub późniejszej, mieć prawidłową jakość i dać się powiązać z urządzeniem D1, komendą C42, generacją 8 i uruchomieniem sterownika, w którym wykonano próbę.
W razie potrzeby przewiń tabelę poziomo.
| Próbka przy ocenie przez sterownik | Decyzja w przykładzie |
|---|---|
| 40,3 mm, 80 ms, poprawna jakość i zgodna próba | Potwierdza spełnienie warunku położenia. |
| 39,5 mm albo 40,5 mm, wiek do 200 ms, pozostałe warunki spełnione | Potwierdza. Granice przedziału są włączone. |
| 40,3 mm, 201 ms albo próbka sprzed rozpoczęcia wykonania | Nie wystarcza do potwierdzenia. |
| 40,3 mm, 80 ms, odczyt z innego uruchomienia bez powiązania z C42 | Nie wystarcza do potwierdzenia. |
Sterownik raportuje, kiedy ocenił próbkę i jaki był wtedy jej wiek. API może później pokazać historyczny wynik bez odejmowania zegara sterownika od własnego. Pojedyncza próbka potwierdza położenie w chwili pomiaru. Wózek mógł już stać w celu przed komendą. Jeżeli trzeba wykazać przemieszczenie, użyj pomiarów przed próbą i w jej trakcie lub po niej. Jeżeli trzeba wykazać utrzymanie położenia, określ czas pozostawania w tolerancji i ewentualny limit prędkości.
Co zrobić ze spóźnioną komendą i drugim operatorem
Termin C42 ogranicza start. API sprawdza go przy przyjęciu, a sterownik ponownie przed wykonaniem. Od chwili now >= expiresAt rozpoczęcie jest niedozwolone. W MQTT 5.0 Message Expiry Interval ogranicza czas życia wiadomości przed przekazaniem przez broker. Wiadomość już dostarczona może jednak czekać w kolejce sterownika. Jeżeli sterownik nie potrafi wiarygodnie ocenić terminu, ten kontrakt wstrzymuje start. Termin rozpoczęcia, limit trwającego ruchu i potwierdzenie zatrzymania to osobne wymagania. MQTT 5.0, § 3.3.2.3.3
Dwóch operatorów widzi generację 7. API atomowo przyjmuje C42 i zwiększa generację do 8. Drugie żądanie ze starą generacją otrzymuje konflikt. Ponowienie tej samej C42 omija tę kontrolę, ponieważ odczytuje już istniejący zasób. Dlatego API najpierw sprawdza uprawnienia i klucz ponowienia, a dopiero dla nowej komendy ocenia termin i oczekiwaną generację.
Przykład dopuszcza jedno nierozstrzygnięte zadanie na urządzenie. Nowa intencja z aktualną generacją czeka na rozstrzygnięcie C42 albo otrzymuje informację, że urządzenie jest zajęte. Sterownik po restarcie zachowuje ochronę przed duplikatem. Spóźniony egzemplarz C42 nadal odwołuje się do tej samej próby. Polityka kolejki i zatrzymania podlega osobnej specyfikacji urządzenia. Funkcje bezpieczeństwa maszyny wymagają osobnego projektu.
Jak sprawdzić kontrakt przed wdrożeniem
Wstrzykuj awarie na poszczególnych granicach. W każdej próbie obserwuj panel, trwały dziennik sterownika i rzeczywisty efekt na urządzeniu.
W razie potrzeby przewiń tabelę poziomo.
| Próba | Oczekiwany wynik |
|---|---|
Utrata 202 po zapisaniu C42 |
Ponowiony POST zwraca ten sam zasób. Powstaje jedno zlecenie. |
| Duplikat po wysyłce lub po restarcie | Sterownik rozpoznaje C42. Liczba startów nie rośnie. |
| Ten sam klucz z celem 60 mm | Konflikt parametrów, bez nowego zlecenia. |
| Dwie równoległe intencje z generacją 7 | Jedna zostaje przyjęta. Drugi operator odświeża stan. |
| Termin mija w kolejce lub sterownik nie może go wiarygodnie ocenić | Brak startu. Stan wygaśnięcia wymaga rozstrzygającej informacji. |
| Zanik zasilania między zapisem startu a wynikiem | Efekt nieznany do uzgodnienia. Restart nie uruchamia ponownie C42. |
| Stary, błędny lub niewłaściwie powiązany pomiar | Brak potwierdzenia C42. |
| Utrata uprawnień do urządzenia | Odmowa także przy odczycie wyniku C42. |
Do odtworzenia próby wystarczą identyfikatory komendy i urządzenia, generacja, aktor, wersja zdarzenia, identyfikator uruchomienia, zapis przyjęcia i startu, pomiar oraz wynik. W dzienniku zapisuj decyzję o autoryzacji bez kopiowania tokenów. W odbiorze integracji sprawdź, czy operator po każdym przerwaniu połączenia widzi dokładnie to, co system potrafi wykazać o C42.
Źródła
- IETF RFC 9110, czerwiec 2022, idempotencja metod HTTP. § 9.2.2
- IETF RFC 9110, czerwiec 2022, semantyka statusu 202. § 15.3.3
- OASIS MQTT 5.0, marzec 2019, czas życia wiadomości i poziomy QoS. Specyfikacja
- AWS Builders’ Library, identyfikatory intencji klienta i ponowienia. Making retries safe with idempotent APIs
- AWS Prescriptive Guidance, spójny zapis operacji i zadania wysyłki oraz duplikaty wiadomości. Transactional outbox pattern
Źródła sprawdzono 10 listopada 2026 r. Przykład jest modelem kontraktu aplikacji. Trwałość zapisu, zachowanie po zaniku zasilania i kryteria ruchu wymagają prób na docelowym urządzeniu.