"""Module containing the knitout executer class"""
from knit_graphs.Knit_Graph import Knit_Graph
from virtual_knitting_machine.Knitting_Machine import Knitting_Machine
from knitout_interpreter.knitout_execution_structures.Carriage_Pass import Carriage_Pass
from knitout_interpreter.knitout_operations.Header_Line import Knitout_Header_Line, Knitting_Machine_Header
from knitout_interpreter.knitout_operations.Knitout_Line import Knitout_Line, Knitout_Comment_Line, Knitout_Version_Line
from knitout_interpreter.knitout_operations.Pause_Instruction import Pause_Instruction
from knitout_interpreter.knitout_operations.needle_instructions import Needle_Instruction
[docs]
class Knitout_Executer:
"""A class used to execute a set of knitout instructions on a virtual knitting machine."""
def __init__(self, instructions: list[Knitout_Line], knitting_machine: Knitting_Machine, accepted_error_types: list | None = None, knitout_version: int = 2):
"""Initialize the knitout executer.
Args:
instructions: List of knitout instructions to execute.
knitting_machine: The virtual knitting machine to execute instructions on.
accepted_error_types: List of exception types that can be resolved by
commenting them out. Defaults to None.
knitout_version: The knitout version to use. Defaults to 2.
"""
self.knitout_version = knitout_version
if accepted_error_types is None:
accepted_error_types = []
self.knitting_machine: Knitting_Machine = knitting_machine
self.instructions: list[Knitout_Line] = instructions
self.process: list[Knitout_Line | Carriage_Pass] = []
self.executed_header: Knitting_Machine_Header = Knitting_Machine_Header(self.knitting_machine)
self.executed_instructions: list[Knitout_Line] = []
self.test_and_organize_instructions(accepted_error_types)
self._carriage_passes: list[Carriage_Pass] = [cp for cp in self.process if isinstance(cp, Carriage_Pass)]
self._left_most_position: int | None = None
self._right_most_position: int | None = None
for cp in self._carriage_passes:
left, right = cp.carriage_pass_range()
if self._left_most_position is None:
self._left_most_position = left
elif left is not None:
self._left_most_position = min(self._left_most_position, left)
if self._right_most_position is None:
self._right_most_position = right
elif right is not None:
self._right_most_position = max(self._right_most_position, right)
@property
def version_line(self) -> Knitout_Version_Line:
"""Get the version line for the executed knitout.
Returns:
The version line for the executed knitout.
"""
return Knitout_Version_Line(self.knitout_version)
@property
def execution_time(self) -> int:
"""Get the execution time as measured by carriage passes.
Returns:
Count of carriage passes in process as a measure of knitting time.
"""
return len(self._carriage_passes)
@property
def left_most_position(self) -> int | None:
"""Get the leftmost needle position used in execution.
Returns:
The position of the left most needle used in execution, or None if
no needles were used.
"""
return self._left_most_position
@property
def right_most_position(self) -> int | None:
"""Get the rightmost needle position used in execution.
Returns:
The position of the right most needle used in the execution, or None
if no needles were used.
"""
return self._right_most_position
@property
def resulting_knit_graph(self) -> Knit_Graph:
"""Get the knit graph resulting from instruction execution.
Returns:
Knit Graph that results from execution of these instructions.
"""
return self.knitting_machine.knit_graph
@property
def carriage_passes(self) -> list[Carriage_Pass]:
"""Get the carriage passes from this execution.
Returns:
The carriage passes resulting from this execution in execution order.
"""
return self._carriage_passes
[docs]
def test_and_organize_instructions(self, accepted_error_types: list | None = None) -> None:
"""Test the given execution and organize the instructions in the class structure.
This method processes all instructions, organizing them into carriage passes
and handling any errors that occur during execution.
Args:
accepted_error_types: A list of exceptions that instructions may throw
that can be resolved by commenting them out. Defaults to None.
"""
if accepted_error_types is None:
accepted_error_types = []
self.process: list[Knitout_Line | Carriage_Pass] = []
self.executed_instructions: list[Knitout_Line] = []
in_header = True
current_pass = None
for instruction in self.instructions:
try:
if instruction.interrupts_carriage_pass:
in_header = False
if isinstance(instruction, Needle_Instruction):
in_header = False
if current_pass is None: # Make a new Carriage Pass from this
current_pass = Carriage_Pass(instruction, self.knitting_machine.rack, self.knitting_machine.all_needle_rack)
else: # Check if instruction can be added to the carriage pass, add it or create a new current carriage pass
was_added = current_pass.add_instruction(instruction, self.knitting_machine.rack, self.knitting_machine.all_needle_rack)
if not was_added:
executed_pass = current_pass.execute(self.knitting_machine)
self.process.append(current_pass)
self.executed_instructions.extend(executed_pass)
current_pass = Carriage_Pass(instruction, self.knitting_machine.rack, self.knitting_machine.all_needle_rack)
elif isinstance(instruction, Knitout_Version_Line):
self.knitout_version = instruction.version
elif isinstance(instruction, Knitout_Header_Line):
_updated = self.executed_header.update_header(instruction, update_machine=in_header) # only update the machine_state if in the header section
else:
if instruction.interrupts_carriage_pass and current_pass is not None: # interrupt the current carriage pass with rack and carrier operations
executed_pass = current_pass.execute(self.knitting_machine)
self.process.append(current_pass)
self.executed_instructions.extend(executed_pass)
current_pass = None
updated = instruction.execute(self.knitting_machine)
if updated or isinstance(instruction, Pause_Instruction):
self.process.append(instruction)
self.executed_instructions.append(instruction)
else:
comment = Knitout_Comment_Line(instruction) # create a no-op comment for this line because it did not cause an update.
self.process.append(comment)
self.executed_instructions.append(comment)
except tuple(accepted_error_types) as e:
error_comment = Knitout_Comment_Line(f"Excluded {type(e).__name__}: {e.message}")
self.process.append(error_comment)
self.executed_instructions.append(error_comment)
comment = Knitout_Comment_Line(instruction)
self.process.append(comment)
self.executed_instructions.append(comment)
if current_pass is not None:
executed_pass = current_pass.execute(self.knitting_machine)
self.process.append(current_pass)
self.executed_instructions.extend(executed_pass)
# add the header and version line to the beginning of the executed instructions
executed_process = self.executed_instructions
self.executed_instructions = self.executed_header.get_header_lines(self.knitout_version)
self.executed_instructions.extend(executed_process)
[docs]
def write_executed_instructions(self, filename: str) -> None:
"""Write a file with the organized knitout instructions.
Args:
filename: The file path to write the executed instructions to.
"""
with open(filename, "w") as file:
file.writelines([str(instruction) for instruction in self.executed_instructions])