diff --git a/src/main/python/main/ayab/engine/engine_fsm.py b/src/main/python/main/ayab/engine/engine_fsm.py index da76db89..0f41814d 100644 --- a/src/main/python/main/ayab/engine/engine_fsm.py +++ b/src/main/python/main/ayab/engine/engine_fsm.py @@ -51,6 +51,7 @@ class State(Enum): REQUEST_TEST = auto() CONFIRM_TEST = auto() RUN_TEST = auto() + DISCONNECT = auto() FINISHED = auto() @@ -196,8 +197,18 @@ def _API6_run_knit(control: Control, operation: Operation) -> Output: if token == Token.reqLine: pattern_finished = control.cnf_line_API6(param) if pattern_finished: - control.state = State.FINISHED - return Output.KNITTING_FINISHED + # When closing the serial port, the final bytes written + # may be dropped by the driver + # (see https://github.com/serialport/serialport-rs/issues/117). + # This may cause the final `cnfLine` response to get lost and the + # firmware to get stuck knitting the previous row + # (see https://github.com/AllYarnsAreBeautiful/ayab-desktop/issues/662). + # To avoid this, before closing the port, we send a `reqInfo` message + # to the firmware and wait for the response. + control.com.req_info() + control.state = State.DISCONNECT + control.logger.debug("State DISCONNECT") + return Output.DISCONNECTING_FROM_MACHINE else: return Output.NEXT_LINE # else @@ -237,6 +248,17 @@ def _API6_run_test(control: Control, operation: Operation) -> Output: control.check_serial_API6() return Output.NONE + @staticmethod + def _API6_disconnect(control: Control, operation: Operation) -> Output: + token, param = control.check_serial_API6() + if token == Token.cnfInfo: + # We received a response to our final `reqInfo` request, + # it is now safe to close the port. + control.state = State.FINISHED + return Output.KNITTING_FINISHED + # else + return Output.NONE + @staticmethod def _API6_finished(control: Control, operation: Operation) -> Output: control.logger.debug("State FINISHED") diff --git a/src/main/python/main/ayab/engine/output.py b/src/main/python/main/ayab/engine/output.py index 972516de..437c9596 100644 --- a/src/main/python/main/ayab/engine/output.py +++ b/src/main/python/main/ayab/engine/output.py @@ -33,6 +33,7 @@ class Output(Enum): ERROR_INVALID_SETTINGS = auto() ERROR_SERIAL_PORT = auto() CONNECTING_TO_MACHINE = auto() + DISCONNECTING_FROM_MACHINE = auto() INITIALIZING_FIRMWARE = auto() WAIT_FOR_INIT = auto() ERROR_WRONG_API = auto() @@ -65,6 +66,9 @@ def _none(self) -> None: def _connecting_to_machine(self) -> None: self.emit_notification("Connecting to machine...", False) + def _disconnecting_from_machine(self) -> None: + self.emit_notification("Disconnecting from machine...") + def _initializing_firmware(self) -> None: self.emit_notification("Initializing firmware")