Skip to content

Commit

Permalink
Fix the resend history after power panic
Browse files Browse the repository at this point in the history
  • Loading branch information
TojikCZ committed May 3, 2024
1 parent 3cd2623 commit 49c2fbf
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 36 deletions.
15 changes: 14 additions & 1 deletion prusa/link/printer_adapter/file_printer.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@
from blinker import Signal # type: ignore

from ..config import Config
from ..const import PRINT_QUEUE_SIZE, QUIT_INTERVAL, STATS_EVERY, TAIL_COMMANDS
from ..const import (
HISTORY_LENGTH,
PRINT_QUEUE_SIZE,
QUIT_INTERVAL,
STATS_EVERY,
TAIL_COMMANDS,
)
from ..serial.helpers import enqueue_instruction, wait_for_instruction
from ..serial.instruction import Instruction
from ..serial.serial_parser import ThreadedSerialParser
Expand Down Expand Up @@ -138,6 +144,8 @@ def _print(self, from_gcode_number=None):
the gcode number to start from. Implies power panic recovery -
goes into pause when the correct gcode number is reached
"""
history_accumulator = []

prctl_name()
total_size = os.path.getsize(self.data.file_path)
with open(self.data.file_path, "r", encoding='utf-8') as file:
Expand All @@ -161,11 +169,16 @@ def _print(self, from_gcode_number=None):
if (self.data.recovering
and from_gcode_number > self.data.gcode_number):
if gcode:
history_from = from_gcode_number - HISTORY_LENGTH
if self.data.gcode_number >= history_from:
history_accumulator.append(gcode)
self.data.gcode_number += 1
continue

# Skip finished, pause here, remove the recovering flag
if self.data.recovering:
history_accumulator.append(gcode)
self.serial_queue.replenish_history(history_accumulator)
self.pause()

# This will make it PRINT_QUEUE_SIZE lines in front of what
Expand Down
39 changes: 38 additions & 1 deletion prusa/link/serial/instruction.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ class Instruction:
def __init__(self,
message: str,
to_checksum: bool = False,
data: Optional[bytes] = None):
data: Optional[bytes] = None,
number: Optional[int] = None,
):
if message.count("\n") != 0:
raise RuntimeError("Instructions cannot contain newlines.")

Expand All @@ -30,6 +32,9 @@ def __init__(self,
# If already sent, this will contain the sent bytes
self.data = data

# If we know our number, it is saved here (used by message history)
self.number = number

# Event set when the write has been _confirmed by the printer
self.confirmed_event = Event()

Expand Down Expand Up @@ -98,6 +103,38 @@ def reset(self):
self.sent_at = None
self.sent_event.clear()

def fill_data(self, message_number: int):
"""
Puts together binary data to send as for the given instruction.
The specific data might contain a message number and a checksum.
Also a newline gets appended at the end
:param instruction: Instruction to get data for
:return: binary data to send
"""
data = self.message.encode("ASCII")
if self.to_checksum:
number_part = f"N{message_number} ".encode("ASCII")
to_checksum = number_part + data + b" "
checksum = self.get_checksum(to_checksum)
checksum_data = f"*{checksum}".encode("ASCII")
data = to_checksum + checksum_data
self.number = message_number
data += b"\n"
self.data = data

@staticmethod
def get_checksum(data: bytes):
"""
Goes over the given bytes and returns a checksum, which is
constructed by XORing each byte of data to a zero
:param data: data to make a checksum out of
:return: the checksum which is a number
"""
checksum = 0
for byte in data:
checksum ^= byte
return checksum


class MatchableInstruction(Instruction):
"""
Expand Down
49 changes: 15 additions & 34 deletions prusa/link/serial/serial_queue.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,37 +233,6 @@ def is_empty(self):

# --- Actual methods ---

def get_data(self, instruction):
"""
Puts together binary data to send as for the given instruction.
The specific data might contain a message number and a checksum.
Also a newline gets appended at the end
:param instruction: Instruction to get data for
:return: binary data to send
"""
data = instruction.message.encode("ASCII")
if instruction.to_checksum:
number_part = f"N{self.message_number} ".encode("ASCII")
to_checksum = number_part + data + b" "
checksum = self.get_checksum(to_checksum)
checksum_data = f"*{checksum}".encode("ASCII")
data = to_checksum + checksum_data
data += b"\n"
return data

@staticmethod
def get_checksum(data: bytes):
"""
Goes over the given bytes and returns a checksum, which is
constructed by XORing each byte of data to a zero
:param data: data to make a checksum out of
:return: the checksum which is a number
"""
checksum = 0
for byte in data:
checksum ^= byte
return checksum

def _hookup_output_capture(self):
"""
Instructions can capture output, this will register the
Expand Down Expand Up @@ -306,7 +275,7 @@ def _send(self):
if self.message_number == MAX_INT:
self._reset_message_number()

instruction.data = self.get_data(instruction)
instruction.fill_data(self.message_number)

# If the instruction is M110 read the value it'll set and save it
m110_match = M110_REGEX.match(instruction.message)
Expand Down Expand Up @@ -338,10 +307,21 @@ def _send(self):
self.serial_adapter.write(self.current_instruction.data)

def set_message_number(self, number):
"""Sets the message number to the given value"""
"""Sets the message number to the given value
Only for power panic recovery"""
with self.write_lock:
self.message_number = number

def replenish_history(self, messages: List[str]):
"""Expects that the message number is set to the current instruction
ought to be sent next"""
from_number = self.message_number - (len(messages) - 1)
self.send_history.clear()
for i, message in enumerate(messages):
instruction = Instruction(message, to_checksum=True)
instruction.fill_data(from_number + i)
self.send_history.append(instruction)

def _enqueue(self, instruction: Instruction, to_front=False):
"""Internal method for enqueuing when already locked"""
if to_front:
Expand Down Expand Up @@ -433,7 +413,8 @@ def _resend(self, count):
instruction = Instruction(
instruction_from_history.message,
to_checksum=True,
data=instruction_from_history.data)
data=instruction_from_history.data,
number=instruction_from_history.number)
self.recovery_list.append(instruction)

def _confirmed(self, force=False):
Expand Down

0 comments on commit 49c2fbf

Please sign in to comment.